library MatchmolDLL;
(*
checkmol/matchmol
Norbert Haider, University of Vienna, 2003, 2004
norbert.haider@univie.ac.at

Win32-DLL porting by:
Alessandro Barozza
PROCOS S.p.A
www.procos.it  -  barozza@procos.it

WHY?
I needed substructure matching capability.
I needed a dll for using with visual basic or VBA (MS-Access).
I needed to pass the mol file as string (from a memo field in a database
and not as a molfile on the disk)
so... I've modified the original matchmol

1)  line 1: subst. "program" with "library"
2)  rem main routine
3)  add "init_globals_DLL": this because the dll won't be unloaded between
    matches and memory for molecule must be instance only 1 time.
    This function is a modification of init_globals original function
4)  add "yet_initialized" variable as boolean in global var section
4a) add "uses windows"
5)  add "beep" procedure at the beginning because beep function in windows is
    different from standard pascal
6)  add "mm_Init_mol" procedure
7)  add "mm_Elab_mol" procedure
8)  add "mm_SetCurrentMolAsQuery" procedure
9)  add "mm_ReadInputLine" procedure
10) add "mm_SetMol" procedure
11) add "mm_getrings"
12) add "mm_getAtomRing"
12a)add "mm_Version"
13) subst all "writeln" with "mod_wrt_ln" because write and writeln function,
     in win32, cause a gpf error
14) subst "write" with "mod_wrt_noln" (see 13)
15) add at the beginning all overload functions for write substitution.
    Write & writeln now write on a string var (see 16)
16) add "gbl_str" in global var section
17) rem body of "mod_wrt_noln_mol" function (because of number format in write
    function not supported by overload functions. The original name of
    "mod_wrt_noln_mol" was "write_mol" but when "write" was substituded with
    mod. function also the name of functions containig "write" are modified
18) rem body of "mod_wrt_noln_needle_mol" function (see 17)
19) add export section (CASE SENSITIVE!!!)
20) substitute "bondtypes_OK" function with a modified version as suggested
    by prof. Haider in an e-mail to me.
21) modify version const from 0.2c to "W32DLL 0.2ca".


COMPILE THE DLL!!! USE ALL OPTIMIZATION TO INCREASE SPEED!!
Compile with fpc (Free Pascal, see http://www.freepascal.org)

fpc MatchMolDLL.pas -S2 -Xs -OG -Or -O2u -Op1 -TWin32

In the original checkmol.pas package is suggested to use -Sd parameter but
it is not compatible with overloading of functions. Use -S2 parameter.

For molecule depicting I use pure-visualbasic code. I only use the checkmol
capability for ring identification to find out and visualize double bonds
in rings. (see the function mm_getatomring)
The visual basic (or vba) declarations for functions in dll are:

--------------------------------------------------------------------------------
Private Declare Sub mm_SetMol Lib "matchmolDLL.dll" (ByVal st As String)
Private Declare Sub mm_SetCurrentMolAsQuery Lib "matchmolDLL.dll" ()
Private Declare Function mm_Match Lib "matchmolDLL.dll" (ByVal Exact As Boolean) As Long

Private Declare Function mm_GetRings Lib "matchmolDLL.dll" () As Long
Private Declare Function mm_GetAtomRing Lib "matchmolDLL.dll" (ByVal AtomNumber As Long) As Long
Private Declare Sub mm_Version Lib "matchmolDLL.dll" (ByVal st As String)
--------------------------------------------------------------------------------
The use is very simple:
--------------------------------------------------------------------------------
Public Function MatchMol(Needle As String, Haystack As String, Optional ExactMatch As Boolean = False) As Boolean
Static oldNeedle As String
    If oldNeedle <> Needle Then
        oldNeedle = Needle
        mm_SetMol Needle
        mm_SetCurrentMolAsQuery
    End If

mm_SetMol Haystack
If mm_Match(ExactMatch) <> 0 Then MatchMol = True

End Function
--------------------------------------------------------------------------------
The needle is not erased after matching so you can perform several matches
without change the needle. When it changes it will be automaticly update in
matchmol DLL. You can use this function also in a SQL query in MS-ACCESS.
The DLL code is very stable and fast (on my pentium4 1400MHZ about
350 structures/sec were matched).
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

ORIGINAL CHECKMOL HEADER:

This software is published under the terms of the GNU General Public
License (GPL, see below). For a detailed description of this license,
see http://www.gnu.org/copyleft/gpl.html

If invoked as "checkmol", this program reads 2D and 3D molecular structure
files in various formats (Tripos Alchemy "mol", Tripos SYBYL "mol2", MDL "mol")
and describes the molecule either by its functional groups or by a set of
descriptors useful for database pre-screening (number of rings, sp2-hybridized
carbons, aromatic bonds, etc.).

If invoked as "matchmol", the program reads two individual 2D or 3D molecular
structure files in various formats (see above) and checks if the first molecule
(the "needle") is a substructure of the second one (the "haystack").
"Haystack" can also be a MDL SD-file (containing multiple MOL files);
if invoked with "-" as file argument, both "needle" and "haystack" are
read as an SD-file from standard input, assuming the first entry in
the SDF to be the "needle"; output: entry number + ":F" (false) or ":T" (true)


Compile with fpc (Free Pascal, see http://www.freepascal.org), using
the -Sd option (Delphi mode; IMPORTANT!)

example for compilation and installation:

fpc -Sd checkmol.pas

as "root", do the following:

cp checkmol /usr/local/bin
cd /usr/local/bin
ln checkmol matchmol


Version history

v0.1   extends "chkmol" utility (N. Haider, 2002), adds matching functionality;

v0.1a  minor bugfixes:
       stop ring search when max_rings is reached (function is_ringpath);
       fixed upper/lowercase of element symbol in MDL molfile output;
       added debug output for checkmol (-D option)

v0.2   added functionality to switch ring search from SAR (set of all rings) to
       SSR (set of small rings; see below) if number of rings exceeds max_rings
       (1024), e.g. with Buckminster-fullerenes (thanks to E.-G. Schmid for a hint);
       added -r command-line option to force ring search into SSR mode;
       as SSR, we regard the set of all rings with max. 10 ring members, and in
       which no ring is completely contained in another one

v0.2a  fixed a bug in function is_ringpath which could cause SAR ring search to
       overlook a few rings (e.g., the six 8-membered rings in cubane)

v0.2b  fixed unequal treatment of needle and haystack structures with respect
       to aromaticity check (trusting input file vs. full check)

v0.2c  modified the changes of v0.2b so that they affect only "matchmol" mode;
       added quick_match function

Credits:

Rami Jbara (rami_jbara@hotmail.com) designed the 8-character functional
group codes


===============================================================================
DISCLAIMER
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, mod_wrt_noln to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
===============================================================================
*)


uses
  SYSUTILS, WINDOWS;


const
  version      = 'W32DLL 0.2ca';
  max_atoms    = 1024;
  max_bonds    = 1024;
  max_ringsize = 128;
  max_rings    = 1024;
  max_fg       = 256;
  TAB          = #26;

  max_matchpath_length = 256;
  pmCheckMol   = 1001;
  pmMatchMol   = 1002;

  rs_sar       = 2001;  // ring search mode: SAR = set of all rings
  rs_ssr       = 2002;  //                   SSR = set of small rings

  // Definitions for functional groups:
  fg_cation                         = 001;
  fg_anion                          = 002;
  fg_carbonyl                       = 003;
  fg_aldehyde                       = 004;
  fg_ketone                         = 005;
  fg_thiocarbonyl                   = 006;
  fg_thioaldehyde                   = 007;
  fg_thioketone                     = 008;
  fg_imine                          = 009;
  fg_hydrazone                      = 010;
  fg_semicarbazone                  = 011;
  fg_thiosemicarbazone              = 012;
  fg_oxime                          = 013;
  fg_oxime_ether                    = 014;
  fg_ketene                         = 015;
  fg_ketene_acetal_deriv            = 016;
  fg_carbonyl_hydrate               = 017;
  fg_hemiacetal                     = 018;
  fg_acetal                         = 019;
  fg_hemiaminal                     = 020;
  fg_aminal                         = 021;
  fg_thiohemiaminal                 = 022;
  fg_thioacetal                     = 023;
  fg_enamine                        = 024;
  fg_enol                           = 025;
  fg_enolether                      = 026;
  fg_hydroxy                        = 027;
  fg_alcohol                        = 028;
  fg_prim_alcohol                   = 029;
  fg_sec_alcohol                    = 030;
  fg_tert_alcohol                   = 031;
  fg_1_2_diol                       = 032;
  fg_1_2_aminoalcohol               = 033;
  fg_phenol                         = 034;
  fg_1_2_diphenol                   = 035;
  fg_enediol                        = 036;
  fg_ether                          = 037;
  fg_dialkylether                   = 038;
  fg_alkylarylether                 = 039;
  fg_diarylether                    = 040;
  fg_thioether                      = 041;
  fg_disulfide                      = 042;
  fg_peroxide                       = 043;
  fg_hydroperoxide                  = 044;
  fg_hydrazine                      = 045;
  fg_hydroxylamine                  = 046;
  fg_amine                          = 047;
  fg_prim_amine                     = 048;
  fg_prim_aliph_amine               = 049;
  fg_prim_arom_amine                = 050;
  fg_sec_amine                      = 051;
  fg_sec_aliph_amine                = 052;
  fg_sec_mixed_amine                = 053;
  fg_sec_arom_amine                 = 054;
  fg_tert_amine                     = 055;
  fg_tert_aliph_amine               = 056;
  fg_tert_mixed_amine               = 057;
  fg_tert_arom_amine                = 058;
  fg_quart_ammonium                 = 059;
  fg_n_oxide                        = 060;
  fg_halogen_deriv                  = 061;
  fg_alkyl_halide                   = 062;
  fg_alkyl_fluoride                 = 063;
  fg_alkyl_chloride                 = 064;
  fg_alkyl_bromide                  = 065;
  fg_alkyl_iodide                   = 066;
  fg_aryl_halide                    = 067;
  fg_aryl_fluoride                  = 068;
  fg_aryl_chloride                  = 069;
  fg_aryl_bromide                   = 070;
  fg_aryl_iodide                    = 071;
  fg_organometallic                 = 072;
  fg_organolithium                  = 073;
  fg_organomagnesium                = 074;
  fg_carboxylic_acid_deriv          = 075;
  fg_carboxylic_acid                = 076;
  fg_carboxylic_acid_salt           = 077;
  fg_carboxylic_acid_ester          = 078;
  fg_lactone                        = 079;
  fg_carboxylic_acid_amide          = 080;
  fg_carboxylic_acid_prim_amide     = 081;
  fg_carboxylic_acid_sec_amide      = 082;
  fg_carboxylic_acid_tert_amide     = 083;
  fg_lactam                         = 084;
  fg_carboxylic_acid_hydrazide      = 085;
  fg_carboxylic_acid_azide          = 086;
  fg_hydroxamic_acid                = 087;
  fg_carboxylic_acid_amidine        = 088;
  fg_carboxylic_acid_amidrazone     = 089;
  fg_nitrile                        = 090;
  fg_acyl_halide                    = 091;
  fg_acyl_fluoride                  = 092;
  fg_acyl_chloride                  = 093;
  fg_acyl_bromide                   = 094;
  fg_acyl_iodide                    = 095;
  fg_acyl_cyanide                   = 096;
  fg_imido_ester                    = 097;
  fg_imidoyl_halide                 = 098;
  fg_thiocarboxylic_acid_deriv      = 099;
  fg_thiocarboxylic_acid            = 100;
  fg_thiocarboxylic_acid_ester      = 101;
  fg_thiolactone                    = 102;
  fg_thiocarboxylic_acid_amide      = 103;
  fg_thiolactam                     = 104;
  fg_imido_thioester                = 105;
  fg_oxohetarene                    = 106;
  fg_thioxohetarene                 = 107;
  fg_iminohetarene                  = 108;
  fg_orthocarboxylic_acid_deriv     = 109;
  fg_carboxylic_acid_orthoester     = 110;
  fg_carboxylic_acid_amide_acetal   = 111;
  fg_carboxylic_acid_anhydride      = 112;
  fg_carboxylic_acid_imide          = 113;
  fg_carboxylic_acid_unsubst_imide  = 114;
  fg_carboxylic_acid_subst_imide    = 115;
  fg_co2_deriv                      = 116;
  fg_carbonic_acid_deriv            = 117;
  fg_carbonic_acid_monoester        = 118;
  fg_carbonic_acid_diester          = 119;
  fg_carbonic_acid_ester_halide     = 120;
  fg_thiocarbonic_acid_deriv        = 121;
  fg_thiocarbonic_acid_monoester    = 122;
  fg_thiocarbonic_acid_diester      = 123;
  fg_thiocarbonic_acid_ester_halide = 124;
  fg_carbamic_acid_deriv            = 125;
  fg_carbamic_acid                  = 126;
  fg_carbamic_acid_ester            = 127;
  fg_carbamic_acid_halide           = 128;
  fg_thiocarbamic_acid_deriv        = 129;
  fg_thiocarbamic_acid              = 130;
  fg_thiocarbamic_acid_ester        = 131;
  fg_thiocarbamic_acid_halide       = 132;
  fg_urea                           = 133;
  fg_isourea                        = 134;
  fg_thiourea                       = 135;
  fg_isothiourea                    = 136;
  fg_guanidine                      = 137;
  fg_semicarbazide                  = 138;
  fg_thiosemicarbazide              = 139;
  fg_azide                          = 140;
  fg_azo_compound                   = 141;
  fg_diazonium_salt                 = 142;
  fg_isonitrile                     = 143;
  fg_cyanate                        = 144;
  fg_isocyanate                     = 145;
  fg_thiocyanate                    = 146;
  fg_isothiocyanate                 = 147;
  fg_carbodiimide                   = 148;
  fg_nitroso_compound               = 149;
  fg_nitro_compound                 = 150;
  fg_nitrite                        = 151;
  fg_nitrate                        = 152;
  fg_sulfuric_acid_deriv            = 153;
  fg_sulfuric_acid                  = 154;
  fg_sulfuric_acid_monoester        = 155;
  fg_sulfuric_acid_diester          = 156;
  fg_sulfuric_acid_amide_ester      = 157;
  fg_sulfuric_acid_amide            = 158;
  fg_sulfuric_acid_diamide          = 159;
  fg_sulfuryl_halide                = 160;
  fg_sulfonic_acid_deriv            = 161;
  fg_sulfonic_acid                  = 162;
  fg_sulfonic_acid_ester            = 163;
  fg_sulfonamide                    = 164;
  fg_sulfonyl_halide                = 165;
  fg_sulfone                        = 166;
  fg_sulfoxide                      = 167;
  fg_sulfinic_acid_deriv            = 168;
  fg_sulfinic_acid                  = 169;
  fg_sulfinic_acid_ester            = 170;
  fg_sulfinic_acid_halide           = 171;
  fg_sulfinic_acid_amide            = 172;
  fg_sulfenic_acid_deriv            = 173;
  fg_sulfenic_acid                  = 174;
  fg_sulfenic_acid_ester            = 175;
  fg_sulfenic_acid_halide           = 176;
  fg_sulfenic_acid_amide            = 177;
  fg_thiol                          = 178;
  fg_alkylthiol                     = 179;
  fg_arylthiol                      = 180;
  fg_phosphoric_acid_deriv          = 181;
  fg_phosphoric_acid                = 182;
  fg_phosphoric_acid_ester          = 183;
  fg_phosphoric_acid_halide         = 184;
  fg_phosphoric_acid_amide          = 185;
  fg_thiophosphoric_acid_deriv      = 186;
  fg_thiophosphoric_acid            = 187;
  fg_thiophosphoric_acid_ester      = 188;
  fg_thiophosphoric_acid_halide     = 189;
  fg_thiophosphoric_acid_amide      = 190;
  fg_phosphonic_acid_deriv          = 191;
  fg_phosphonic_acid                = 192;
  fg_phosphonic_acid_ester          = 193;
  fg_phosphine                      = 194;
  fg_phosphinoxide                  = 195;
  fg_boronic_acid_deriv             = 196;
  fg_boronic_acid                   = 197;
  fg_boronic_acid_ester             = 198;
  fg_alkene                         = 199;
  fg_alkyne                         = 200;
  fg_aromatic                       = 201;
  fg_heterocycle                    = 202;
  fg_alpha_aminoacid                = 203;
  fg_alpha_hydroxyacid              = 204;


type
  str2 = string[2];
  str3 = string[3];
  str4 = string[4];
  str5 = string[5];
  str8 = string[8];
  atom_rec  = record
                element : str2;
                atype : str3;
                x : single;
                y : single;
                z : single;
                formal_charge : integer;
                real_charge : single;
                Hexp : smallint;  // explicit H count
                Htot : smallint;  // total H count
                neighbor_count : integer;
                ring_count : integer;
                arom : boolean;
             end;
  bond_rec  = record
                a1 : integer;
                a2 : integer;
                btype : char;
                ring_count : integer;
                arom : boolean;
              end;
  ringpath_type = array[1..max_ringsize] of integer;
  matchpath_type = array[1..max_matchpath_length] of integer;

  atomlist = array[1..max_atoms] of atom_rec;
  bondlist = array[1..max_bonds] of bond_rec;
  ringlist = array[1..max_rings] of ringpath_type;
  neighbor_rec = array[1..8] of integer;
  fglist   = array[1..max_fg] of boolean;

  molbuftype = array[1..(max_atoms+max_bonds+8192)] of string;


  matchmatrix = array[1..4,1..4] of boolean;

  molstat_rec = record
                  n_QA, n_QB, n_chg : integer;                  // number of query atoms, query bonds, charges
                  n_C1, n_C2, n_C : integer;                    // number of sp, sp2 hybridized, and total no. of carbons
                  n_CHB1p, n_CHB2p, n_CHB3p, n_CHB4 : integer;  // number of C atoms with at least 1, 2, 3 hetero bonds
                  n_O2, n_O3 : integer;                         // number of sp2 and sp3 oxygens
                  n_N1, n_N2, n_N3 : integer;                   // number of sp, sp2, and sp3 nitrogens
                  n_S, n_SeTe : integer;                        // number of sulfur atoms and selenium or tellurium atoms
                  n_F, n_Cl, n_Br, n_I : integer;               // number of fluorine, chlorine, bromine, iodine atoms
                  n_P, n_B : integer;                           // number of phosphorus and boron atoms
                  n_Met, n_X : integer;                         // number of metal and hetero atoms
                  n_b1, n_b2, n_b3, n_bar : integer;            // number single, double, triple, and aromatic bonds
                  n_C1O, n_C2O, n_CN, n_XY : integer;           // number of C-O single bonds, C=O double bonds, CN bonds (any type), hetero/hetero bonds
                  n_r3, n_r4, n_r5, n_r6, n_r7, n_r8 : integer; // number of 3-, 4-, 5-, 6-, 7-, and 8-membered rings
                  n_r9, n_r10, n_r11, n_r12, n_r13p : integer;  // number of 9-, 10-, 11-, 12-, and 13plus-membered rings
                  n_rN, n_rN1, n_rN2, n_rN3p : integer;         // number of rings containing N (any number), 1 N, 2 N, and 3 N or more
                  n_rO, n_rO1, n_rO2p : integer;                // number of rings containing O (any number), 1 O, and 2 O or more
                  n_rS, n_rX, n_rAr  : integer;                 // number of rings containing S (any number), any heteroatom (any number), number of aromatic rings
                end;


var
  yet_initialized:boolean;
  gbl_str: string;
  progmode      : integer;
  progname      : string;
  i             : integer;  // general purpose index
  li            : longint;
  opt_none      : boolean;
  opt_verbose   : boolean;
  opt_text      : boolean;
  opt_text_de   : boolean;
  opt_code      : boolean;
  opt_bin       : boolean;
  opt_bitstring : boolean;
  opt_stdin     : boolean;
  opt_exact     : boolean;
  opt_debug     : boolean;
  opt_molout    : boolean;
  opt_molstat   : boolean;
  opt_molstat_X : boolean;
  opt_xmdlout   : boolean;
  filetype : string;
  molfile : text;
  molfilename : string;
  ndl_molfilename : string;
  molname : string;
  ndl_molname : string;
  molcomment  : string;
  n_atoms : integer;
  n_bonds : integer;
  n_rings : integer;    // the number of rings we determined ourselves
  n_cmrings : integer;  // the number of rings we read from a (CheckMol-tweaked) MDL molfile
  n_charges : integer;  // number of charges
  n_heavyatoms : integer;
  n_heavybonds : integer;
  ndl_n_atoms : integer;
  ndl_n_bonds : integer;
  ndl_n_rings : integer;
  ndl_n_heavyatoms : integer;
  ndl_n_heavybonds : integer;
  cm_mdlmolfile  : boolean;
  found_arominfo : boolean;
  found_querymol : boolean;

  atom : ^atomlist;
  bond : ^bondlist;
  ring : ^ringlist;

  ndl_atom : ^atomlist;
  ndl_bond : ^bondlist;
  ndl_ring : ^ringlist;

  matchresult : boolean;
  ndl_matchpath : matchpath_type;
  hst_matchpath : matchpath_type;

  fg   : fglist;
  atomtype : str4;
  newatomtype : str3;

  molbuf : ^molbuftype;
  molbufindex : integer;

  mol_in_queue : boolean;
  mol_count    : longint;

  molstat      : molstat_rec;
  ndl_molstat  : molstat_rec;

  ringsearch_mode : integer;
  max_vringsize   : integer;  // for SSR ring search


// auxiliary functions & procedures


{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}


Function mod_IntToStr (I : Longint) : String;

Var S : String; 

begin 
 Str (I,S);
 mod_IntToStr:=S;
end; 


Function mod_SingleToStr (I : Single) : String;

Var S : String; 

begin 
 Str (I:9:4,S); 
 mod_SingleToStr:=S;
end; 


procedure Mod_wrt_noln(i:single);
begin
gbl_str := gbl_str + mod_singletostr(i);

end; 

procedure Mod_wrt_ln(i:integer);
begin
gbl_str := gbl_str + inttostr(i);

end; 

procedure Mod_wrt_noln(st: string);
begin
gbl_str := gbl_str + st;
end;


procedure Mod_wrt_ln(st: string);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (#13);
end;
//Mod_wrt_noln(i,' ',atom^[i].element,' ',atom^[i].atype,' ',atom^[i].x:9:4,' ',atom^[i].y:9:4,' ');

procedure Mod_wrt_noln(i:integer;st:string;ste:string;st2:string;
                       stb:string;st3:string;i4:single;st4:string;
                       i5:single;st5:string);
begin
Mod_wrt_noln (i);
Mod_wrt_noln (st);
Mod_wrt_noln (ste);
Mod_wrt_noln (st2);
Mod_wrt_noln (stb);
Mod_wrt_noln (st3);

Mod_wrt_noln (i4);
Mod_wrt_noln (st4);
Mod_wrt_noln (i5);
Mod_wrt_noln (st5);

end;
//Mod_wrt_noln(i,' ',bond^[i].a1,' ',bond^[i].a2,' ',bond^[i].btype);
procedure Mod_wrt_noln(i:integer;st:string;i2:integer;st2:string;i3:integer;st3:string;st4:string);
begin
Mod_wrt_noln (i);
Mod_wrt_noln (st);
Mod_wrt_noln (i2);
Mod_wrt_noln (st2);
Mod_wrt_noln (i3);
Mod_wrt_noln (st3);
Mod_wrt_noln (st4);

end;      

procedure Mod_wrt_noln(st:string;i:integer;st2:string);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (i);
Mod_wrt_noln (st2);


end;      

procedure Mod_wrt_ln(st:string;i:integer;st2:string);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (i);
Mod_wrt_noln (st2);
Mod_wrt_noln (#13);

end;      

procedure Mod_wrt_noln(i:integer;st:string);
begin
Mod_wrt_noln (i);
Mod_wrt_noln (st);
Mod_wrt_noln (#13);

end; 

procedure Mod_wrt_noln(st:string;st2:string);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (st2);
end;


procedure Mod_wrt_ln(st: string; i:integer);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (i);
Mod_wrt_noln (#13);
end;


procedure Mod_wrt_noln(st: string; i:integer);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (i);
end;

procedure Mod_wrt_ln(st: string; st2:string);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (st2);
Mod_wrt_noln (#13);
end;

procedure Mod_wrt_ln(st: string; i:integer;st2: string; i2:integer;st3: string; i3:integer);
begin
Mod_wrt_noln (st);
Mod_wrt_noln (i);
Mod_wrt_noln (st2);
Mod_wrt_noln (i2);
Mod_wrt_noln (st3);
Mod_wrt_noln (i3);
Mod_wrt_noln (#13);


end;
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}
{****************************************************************************}














Procedure Beep();
begin
//beep(800,100);

end;

procedure init_globals;
var
  i : integer;
begin
  opt_verbose     := false;
  opt_debug       := false;
  opt_exact       := false;
  opt_stdin       := false;
  opt_text        := false;
  opt_code        := false;
  opt_bin         := false;
  opt_bitstring   := false;
  opt_molout      := false;
  opt_molstat     := false;
  opt_molstat_X   := false;
  opt_xmdlout     := false;
  cm_mdlmolfile   := false;
  found_arominfo  := false;
  found_querymol  := false;
  ringsearch_mode := rs_sar;
  for i := 1 to max_fg do fg[i] := false;
  try
    getmem(molbuf,sizeof(molbuftype));
  except
    on e:Eoutofmemory do
      begin
        mod_wrt_ln('Not enough memory');
        halt(4);
      end;
  end;
end;


procedure init_molstat(var mstat:molstat_rec);
begin
  with mstat do
    begin
      n_QA := 0; n_QB := 0; n_chg := 0;
      n_C1 := 0; n_C2 := 0; n_C  := 0;
      n_CHB1p := 0; n_CHB2p := 0; n_CHB3p := 0; n_CHB4 := 0;
      n_O2 := 0; n_O3  := 0;
      n_N1 := 0; n_N2 := 0; n_N3 := 0;
      n_S := 0; n_SeTe := 0;
      n_F := 0; n_Cl := 0; n_Br := 0; n_I := 0;
      n_P := 0; n_B := 0;
      n_Met := 0; n_X := 0;
      n_b1 := 0; n_b2 := 0; n_b3 := 0; n_bar := 0;
      n_C1O := 0; n_C2O := 0; n_CN := 0; n_XY := 0;
      n_r3 := 0; n_r4 := 0; n_r5 := 0; n_r6 := 0; n_r7 := 0;
      n_r8 := 0; n_r9 := 0; n_r10 := 0; n_r11 := 0; n_r12 := 0; n_r13p := 0;
      n_rN := 0; n_rN1 := 0; n_rN2 := 0; n_rN3p := 0;
      n_rO := 0; n_rO1 := 0; n_rO2p := 0;
      n_rS := 0; n_rX := 0;
      n_rAr := 0;
    end;
end;


procedure debugoutput(dstr:string);
begin
  if opt_debug then mod_wrt_ln(dstr);
end;


procedure left_trim(var trimstr:string);
begin
  while (length(trimstr)>0) and ((trimstr[1]=' ') or (trimstr[1]=TAB)) do
    delete(trimstr,1,1);
end;


function left_int(var trimstr:string):integer;
var
  numstr : string;
  auxstr : string;
  auxint, code : integer;
begin
  numstr := '-+0123456789';
  auxstr := '';
  auxint := 0;
  while (length(trimstr)>0) and ((trimstr[1]=' ') or (trimstr[1]=TAB)) do
    delete(trimstr,1,1);
  while (length(trimstr)>0) and (pos(trimstr[1],numstr)>0) do
    begin
      auxstr := auxstr + trimstr[1];
      delete(trimstr,1,1);
    end;
  val(auxstr,auxint,code);
  left_int := auxint;
end;


procedure show_usage;
begin
  if progmode = pmMatchMol then
    begin
      mod_wrt_ln('matchmol version ',version);
      mod_wrt_ln('Usage: matchmol [options] <needle> <haystack>');
      mod_wrt_ln(' where <needle> and <haystack> are the two molecules to compare');
      mod_wrt_ln(' (supported formats: MDL *.mol, Alchemy *.mol, Sybyl *.mol2)');
      mod_wrt_ln(' options can be:');
      mod_wrt_ln('    -v  verbose output');
      mod_wrt_ln('    -x  exact match');
      mod_wrt_ln('    -r  force SSR (set of small rings) ring search mode');
      mod_wrt_ln('    -m  mod_wrt_noln matching molecule as MDL molfile to standard output');
      mod_wrt_ln('        (default output: record number + ":T" for hit  or ":F" for miss');
    end else
    begin
      mod_wrt_ln('checkmol version ',version);
      mod_wrt_ln('Usage: checkmol [options] <filename>');
      mod_wrt_ln(' where options can be:');
      mod_wrt_ln('    -v  verbose output');
      mod_wrt_ln('  and one of the following:');
      mod_wrt_ln('    -e  english text (common name of functional group; default)');
      mod_wrt_ln('    -d  german text (common name of functional group)');
      mod_wrt_ln('    -c  code (acronym-like code for functional group)');
      mod_wrt_ln('    -b  binary (a bitstring representing absence or presence of each group)');
      mod_wrt_ln('    -s  (the ASCII representation of the above bitstring, i.e. 0s and 1s)');
      mod_wrt_ln('    -x  print molecular statistics (number of various atom types, bond types, ring sizes, etc.)');
      mod_wrt_ln('    -X  same as above, listing all records (even if zero) as comma-separated list');
      mod_wrt_ln('    -m  mod_wrt_noln MDL molfile (with special encoding for aromatic atoms/bonds)');
      mod_wrt_ln('    -r  force SSR (set of small rings) ring search mode');
      mod_wrt_ln(' options can be combined like -vc');
      mod_wrt_ln(' <filename> specifies any file in the formats supported');
      mod_wrt_ln(' (MDL *.mol, Alchemy *.mol, Sybyl *.mol2), the filename "-" (without quotes)');
      mod_wrt_ln(' specifies standard input');
    end;
end;


procedure parse_args;
var
  p : integer;
  parstr : string;
  tmpstr : string;
  //is_option : boolean;
  l : integer;
begin
  tmpstr := '';
  opt_none := true;
  if progmode = pmCheckMol then
    begin
      for p := 1 to paramcount do
        begin
          parstr := paramstr(p);
          if p < paramcount then
            begin
              if (pos('-',parstr)=1) and (p < (paramcount)) then
                begin
                  //is_option := true;
                  tmpstr := paramstr(p);
                  left_trim(tmpstr);
                  l := 0;
                  if pos('v',tmpstr) > 0 then inc(l);
                  if pos('D',tmpstr) > 0 then inc(l);
                  if pos('r',tmpstr) > 0 then inc(l);
                  if (length(tmpstr) > (2 + l)) then
                     begin
                       show_usage;
                       halt(1);
                     end;
                  opt_none := false;
                  if pos('v',tmpstr)>0 then opt_verbose     := true;
                  if pos('D',tmpstr)>0 then opt_debug       := true;
                  if pos('e',tmpstr)>0 then opt_text        := true else
                    begin
                      if pos('d',tmpstr)>0 then opt_text_de := true else
                        begin
                          if pos('c',tmpstr)>0 then opt_code    := true else
                            begin
                              if pos('b',tmpstr)>0 then opt_bin := true else
                              if pos('s',tmpstr)>0 then opt_bitstring := true;
                            end;
                        end;
                      if pos('x',tmpstr)>0 then opt_molstat := true;
                      if pos('r',tmpstr)>0 then ringsearch_mode := rs_ssr;
                      if pos('X',tmpstr)>0 then
                        begin
                          opt_molstat   := true;
                          opt_molstat_X := true;
                          //mod_wrt_ln('====================================');
                        end;
                      if pos('m',tmpstr)>0 then
                        begin
                          opt_text := false;
                          opt_text_de := false;
                          opt_bin := false;
                          opt_bitstring := false;
                          opt_code := false;
                          opt_molstat := false;
                          opt_xmdlout := true;
                        end;
                    end;
                  molfilename := tmpstr;
                end;
            end else
            begin
              if (pos('-',parstr)=1) then
                begin
                  if length(parstr)>1 then
                    begin
                      show_usage;
                      halt(1);
                    end else
                    begin
                      opt_stdin := true;
                    end;
                end else
                  begin
                    opt_stdin := false;
                    molfilename := parstr;
                  end;
            end;
        end;
      if (opt_text = false) and (opt_text_de = false) and (opt_code = false) and
         (opt_bin = false) and  (opt_bitstring = false) and (opt_molstat = false) and
         (opt_molstat_X = false) and (opt_xmdlout = false) then opt_none := true;
    end;
  if progmode = pmMatchMol then
    begin
      ndl_molfilename := '';
      molfilename := '';
      for p := 1 to paramcount do
        begin
          parstr := paramstr(p);
          if p = 1 then
            begin
              if (pos('-',parstr)=1) then
                begin
                  if pos('v',parstr)>1 then opt_verbose     := true;
                  if pos('D',parstr)>1 then opt_debug       := true;
                  if pos('x',parstr)>1 then opt_exact       := true;
                  if pos('m',parstr)>1 then opt_molout      := true;
                  if pos('r',parstr)>1 then ringsearch_mode := rs_ssr;
                  if pos('h',parstr)>1 then
                    begin
                      show_usage;
                      halt(0);
                    end;
                end else ndl_molfilename := parstr;
              end;
          if p = (paramcount - 1) then
            begin
              if (pos('-',parstr)=0) then ndl_molfilename := parstr;
            end;
          if p = paramcount then
            begin
              if parstr <> '-' then molfilename := parstr else opt_stdin := true;
            end;
        end;
    end;  // progmode = pmMatchMol
end;


// input-related functions & procedures


function get_filetype(f:string):string;
var
  rline : string;
  auxstr : string;
  i : integer;
  mdl1 : boolean;
  ri : integer;
  sepcount : integer;
begin
  auxstr := 'unknown';
  i := li; mdl1 := false;
  ri := li -1;
  sepcount := 0;
  while (ri < molbufindex) and (sepcount < 1) do
    begin
      inc(ri);
      rline := molbuf^[ri];
      if (pos('$$$$',rline)>0) then inc(sepcount);
      if (i = li) and (copy(rline,7,5)='ATOMS')
                 and (copy(rline,20,5)='BONDS')
                 and (copy(rline,33,7)='CHARGES') then
        begin
          auxstr := 'alchemy';
        end;
      if (i = li+3) // and (copy(rline,31,3)='999')
                 and (copy(rline,35,5)='V2000')      then mdl1 := true;
      if (i = li+1) and (copy(rline,3,6)='-ISIS-')      then mdl1 := true;
      if (i = li+1) and (copy(rline,3,8)='WLViewer')    then mdl1 := true;
      if (i = li+1) and (copy(rline,3,8)='CheckMol')    then mdl1 := true;
      if (i = li+1) and (copy(rline,3,8)='CATALYST') then
        begin
          mdl1 := true;
          auxstr := 'mdl';
        end;
      if (pos('M  END',rline)=1) or mdl1 then
        begin
          //mod_wrt_ln('MDL format');
          auxstr := 'mdl';
        end;
      if pos('@<TRIPOS>MOLECULE',rline)>0 then
        begin
          auxstr := 'sybyl';
        end;
      inc(i);
    end;
  get_filetype := auxstr;
end;


procedure zap_molecule;
begin
  try
    if atom <> nil then freemem(atom,n_atoms*sizeof(atom_rec));
    if bond <> nil then freemem(bond,n_bonds*sizeof(bond_rec));
    if ring <> nil then freemem(ring,sizeof(ringlist));
  except
    on e:Einvalidpointer do begin end;
  end;
  n_atoms := 0;
  n_bonds := 0;
  n_rings := 0;
end;


procedure zap_needle;
begin
  try
    if ndl_atom <> nil then freemem(ndl_atom,ndl_n_atoms*sizeof(atom_rec));
    if ndl_bond <> nil then freemem(ndl_bond,ndl_n_bonds*sizeof(bond_rec));
    if ndl_ring <> nil then freemem(ndl_ring,sizeof(ringlist));
  except
    on e:Einvalidpointer do begin end;
  end;
  ndl_n_atoms := 0;
  ndl_n_bonds := 0;
  ndl_n_rings := 0;
end;


function is_heavyatom(id:integer):boolean;
var
  r  : boolean;
  el : str2;
begin
  r  := true;
  el := atom^[id].element;
  if (el = 'H ') or (el = 'DU') or (el = 'LP') then r := false;
  is_heavyatom := r;
end;


function is_ndl_heavyatom(id:integer):boolean;
var
  r  : boolean;
  el : str2;
begin
  r  := true;
  el := ndl_atom^[id].element;
  if (el = 'H ') or (el = 'DU') or (el = 'LP') then r := false;
  is_ndl_heavyatom := r;
end;


function is_metal(id:integer):boolean;
var
  r  : boolean;
  el : str2;
begin
  r  := false;
  el := atom^[id].element;
  if (el = 'NA') or (el = 'K ') or (el = 'LI') or (el = 'MG') or
     (el = 'ZN') or (el = 'SN') or (el = 'PD') or (el = 'FE') or
     (el = 'AL') or (el = 'CU') or (el = 'CO') or (el = 'CR') or
     (el = 'HG')                                       // etc. etc.
    then r := true;
  is_metal := r;
end;


function convert_type(oldtype : str4):str3;
var
  i : integer;
  newtype : str3;
begin
  newtype := copy(oldtype,1,3);
  for i := 1 to 3 do newtype[i] := upcase(newtype[i]);
  if newtype[1] = '~' then newtype := 'VAL';
  If newtype[1] = '*' then newtype := 'STR';
  convert_type := newtype;
end;


function convert_sybtype(oldtype : str5):str3;
var
  newtype : str3;
begin
//  NewType := Copy(OldType,1,3);
//  For i := 1 To 3 Do NewType[i] := UpCase(NewType[i]);
//  If NewType[1] = '~' Then NewType := 'VAL';
//  If NewType[1] = '*' Then NewType := 'STR';
  newtype := 'DU ';
  if oldtype = 'H    ' then newtype := 'H  ';
  if oldtype = 'C.ar ' then newtype := 'CAR';
  if oldtype = 'C.2  ' then newtype := 'C2 ';
  if oldtype = 'C.3  ' then newtype := 'C3 ';
  if oldtype = 'C.1  ' then newtype := 'C1 ';
  if oldtype = 'O.2  ' then newtype := 'O2 ';
  if oldtype = 'O.3  ' then newtype := 'O3 ';
  if oldtype = 'O.co2' then newtype := 'O2 ';
  if oldtype = 'O.spc' then newtype := 'O3 ';
  if oldtype = 'O.t3p' then newtype := 'O3 ';
  if oldtype = 'N.1  ' then newtype := 'N1 ';
  if oldtype = 'N.2  ' then newtype := 'N2 ';
  if oldtype = 'N.3  ' then newtype := 'N3 ';
  if oldtype = 'N.pl3' then newtype := 'NPL';
  if oldtype = 'N.4  ' then newtype := 'N3+';
  if oldtype = 'N.am ' then newtype := 'NAM';
  if oldtype = 'N.ar ' then newtype := 'NAR';
  if oldtype = 'F    ' then newtype := 'F  ';
  if oldtype = 'Cl   ' then newtype := 'CL ';
  if oldtype = 'Br   ' then newtype := 'BR ';
  if oldtype = 'I    ' then newtype := 'I  ';
  if oldtype = 'Al   ' then newtype := 'AL ';
  if oldtype = 'ANY  ' then newtype := 'A  ';
  if oldtype = 'Ca   ' then newtype := 'CA ';
  if oldtype = 'Du   ' then newtype := 'DU ';
  if oldtype = 'Du.C ' then newtype := 'DU ';
  if oldtype = 'H.spc' then newtype := 'H  ';
  if oldtype = 'H.t3p' then newtype := 'H  ';
  if oldtype = 'HAL  ' then newtype := 'Cl ';
  if oldtype = 'HET  ' then newtype := 'Q  ';
  if oldtype = 'HEV  ' then newtype := 'DU ';
  if oldtype = 'K    ' then newtype := 'K  ';
  if oldtype = 'Li   ' then newtype := 'LI ';
  if oldtype = 'LP   ' then newtype := 'LP ';
  if oldtype = 'Na   ' then newtype := 'NA ';
  if oldtype = 'P.3  ' then newtype := 'P3 ';
  if oldtype = 'S.2  ' then newtype := 'S2 ';
  if oldtype = 'S.3  ' then newtype := 'S3 ';
  if oldtype = 'S.o  ' then newtype := 'SO ';
  if oldtype = 'S.o2 ' then newtype := 'SO2';
  if oldtype = 'Si   ' then newtype := 'SI ';
  if oldtype = 'P.4  ' then newtype := 'P4 ';
  convert_sybtype := newtype;
end;


function convert_MDLtype(oldtype : str3):str3;
var
  newtype : str3;
begin
//  NewType := Copy(OldType,1,3);
//  For i := 1 To 3 Do NewType[i] := UpCase(NewType[i]);
//  If NewType[1] = '~' Then NewType := 'VAL';
//  If NewType[1] = '*' Then NewType := 'STR';
  newtype := 'DU ';
  if oldtype = 'H  ' then newtype := 'H  ';
  if oldtype = 'C  ' then newtype := 'C3 ';
  if oldtype = 'O  ' then newtype := 'O2 ';
  if oldtype = 'N  ' then newtype := 'N3 ';
  if oldtype = 'F  ' then newtype := 'F  ';
  if oldtype = 'Cl ' then newtype := 'CL ';
  if oldtype = 'Br ' then newtype := 'BR ';
  if oldtype = 'I  ' then newtype := 'I  ';
  if oldtype = 'Al ' then newtype := 'AL ';
  if oldtype = 'ANY' then newtype := 'A  ';
  if oldtype = 'Ca ' then newtype := 'CA ';
  if oldtype = 'Du ' then newtype := 'DU ';
  if oldtype = 'K  ' then newtype := 'K  ';
  if oldtype = 'Li ' then newtype := 'LI ';
  if oldtype = 'LP ' then newtype := 'LP ';
  if oldtype = 'Na ' then newtype := 'NA ';
  if oldtype = 'P  ' then newtype := 'P3 ';
  if oldtype = 'S  ' then newtype := 'S3 ';
  if oldtype = 'Si ' then newtype := 'SI ';
  if oldtype = 'P  ' then newtype := 'P4 ';
  if oldtype = 'A  ' then newtype := 'A  ';
  if oldtype = 'Q  ' then newtype := 'Q  ';
  convert_MDLtype := NewType;
end;


function get_element(oldtype:str4):str2;
var
  elemstr : string;
begin
  if oldtype = 'H   ' then elemstr := 'H ';
  if oldtype = 'CAR ' then elemstr := 'C ';
  if oldtype = 'C2  ' then elemstr := 'C ';
  if oldtype = 'C3  ' then elemstr := 'C ';
  if oldtype = 'C1  ' then elemstr := 'C ';
  if oldtype = 'O2  ' then elemstr := 'O ';
  if oldtype = 'O3  ' then elemstr := 'O ';
  if oldtype = 'O2  ' then elemstr := 'O ';
  if oldtype = 'O3  ' then elemstr := 'O ';
  if oldtype = 'O3  ' then elemstr := 'O ';
  if oldtype = 'N1  ' then elemstr := 'N ';
  if oldtype = 'N2  ' then elemstr := 'N ';
  if oldtype = 'N3  ' then elemstr := 'N ';
  if oldtype = 'NPL ' then elemstr := 'N ';
  if oldtype = 'N3+ ' then elemstr := 'N ';
  if oldtype = 'NAM ' then elemstr := 'N ';
  if oldtype = 'NAR ' then elemstr := 'N ';
  if oldtype = 'F   ' then elemstr := 'F ';
  if oldtype = 'CL  ' then elemstr := 'CL';
  if oldtype = 'BR  ' then elemstr := 'BR';
  if oldtype = 'I   ' then elemstr := 'I ';
  if oldtype = 'AL  ' then elemstr := 'AL';
  if oldtype = 'DU  ' then elemstr := 'DU';
  if oldtype = 'CA  ' then elemstr := 'CA';
  if oldtype = 'DU  ' then elemstr := 'DU';
  if oldtype = 'Cl  ' then elemstr := 'CL';
  if oldtype = 'K   ' then elemstr := 'K ';
  if oldtype = 'LI  ' then elemstr := 'LI';
  if oldtype = 'LP  ' then elemstr := 'LP';
  if oldtype = 'NA  ' then elemstr := 'NA';
  if oldtype = 'P3  ' then elemstr := 'P ';
  if oldtype = 'S2  ' then elemstr := 'S ';
  if oldtype = 'S3  ' then elemstr := 'S ';
  if oldtype = 'SO  ' then elemstr := 'S ';
  if oldtype = 'SO2 ' then elemstr := 'S ';
  if oldtype = 'SI  ' then elemstr := 'SI';
  if oldtype = 'P4  ' then elemstr := 'P ';
  if oldtype = 'A   ' then elemstr := 'A ';
  if oldtype = 'Q   ' then elemstr := 'Q ';
  get_element := elemstr;
end;


function get_sybelement(oldtype:str5):str2;
var
  i : integer;
  elemstr : str2;
begin
  if pos('.',oldtype)<2 then
    begin
      elemstr := copy(oldtype,1,2);
    end else
    begin
      elemstr := copy(oldtype,1,pos('.',oldtype)-1);
      if length(elemstr)<2 then elemstr := elemstr+' ';
    end;
  for i := 1 to 2 do elemstr[i] := upcase(elemstr[i]);
  get_sybelement := elemstr;
end;


function get_MDLelement(oldtype:str3):str2;
var
  i : integer;
  elemstr : str2;
begin
  elemstr := copy(oldtype,1,2);
  for i := 1 to 2 do elemstr[i] := upcase(elemstr[i]);
  if elemstr[1] = '~' then elemstr := '??';
  If elemstr[1] = '*' then elemstr := '??';
  get_MDLelement := elemstr;
end;


procedure read_molfile(mfilename:string);  // reads ALCHEMY mol files
var
  n, code : integer;
  rline, tmpstr : string;
  xstr, ystr, zstr, chgstr : string;
  xval, yval, zval, chgval : single;
  a1str, a2str, elemstr : string;
  a1val, a2val : integer;
  ri : integer;
begin
  if n_atoms > 0 then zap_molecule;
  ri := li;
  rline := molbuf^[ri];
  tmpstr := copy(rline,1,5);
  val(tmpstr,n_atoms,code);
  tmpstr := copy(rline,14,5);
  val(tmpstr,n_bonds,code);
  molname := copy(rline,42,length(rline)-42);
  try
    getmem(atom,n_atoms*sizeof(atom_rec));
    getmem(bond,n_bonds*sizeof(bond_rec));
    getmem(ring,sizeof(ringlist));
  except
    on e:Eoutofmemory do
      begin
        mod_wrt_ln('Not enough memory');
        halt(4);
      end;
  end;
  n_heavyatoms := 0;
  n_heavybonds := 0;
  for n := 1 to n_atoms do
    begin
      with atom^[n] do
        begin
          formal_charge  := 0;
          real_charge    := 0;
          Hexp           := 0;
          Htot           := 0;
          neighbor_count := 0;
          ring_count     := 0;
          arom           := FALSE;
        end;
      inc(ri);
      rline := molbuf^[ri];
      atomtype := copy(rline,7,4);
      elemstr  := get_element(atomtype);
      newatomtype := convert_type(atomtype);
      xstr := copy(rline,14,7);
      ystr := copy(rline,23,7);
      zstr := copy(rline,32,7);
      chgstr := copy(rline,43,7);
      val(xstr,xval,code);
      val(ystr,yval,code);
      val(zstr,zval,code);
      val(chgstr,chgval,code);
      with atom^[n] do
        begin
          element := elemstr;
          atype := newatomtype;
          x := xval; y := yval; z := zval; real_charge := chgval;
        end;
      if is_heavyatom(n) then inc(n_heavyatoms);  
    end;
  for n := 1 to n_bonds do
    begin
      inc(ri);
      rline := molbuf^[ri];
      a1str := copy(rline,9,3);
      a2str := copy(rline,15,3);
      val(a1str,a1val,code);
      if code <> 0 then beep;
      val(a2str,a2val,code);
      if code <> 0 then beep;
      with bond^[n] do
        begin
          a1 := a1val; a2 := a2val; btype := rline[20];
          ring_count := 0; arom := false;
        end;
      if is_heavyatom(a1val) and is_heavyatom(a2val) then inc(n_heavybonds);
    end;
  fillchar(ring^,sizeof(ringlist),0);
  li := ri + 1;
end;


procedure read_mol2file(mfilename:string);  // reads SYBYL mol2 files
var
  n, code : integer;
  sybatomtype : string[5];
  tmpstr, rline : string;
  xstr, ystr, zstr, chgstr : string;
  xval, yval, zval, chgval : single;
  a1str, a2str, elemstr : string;
  a1val, a2val : integer;
  ri : integer;
begin
  if n_atoms > 0 then zap_molecule;
  rline := '';
  ri := li -1;
  while (ri < molbufindex) and (pos('@<TRIPOS>MOLECULE',rline)=0) do
    begin
      inc(ri);
      rline := molbuf^[ri];
    end;
  if ri < molbufindex then
    begin
      inc(ri);
      molname := molbuf^[ri];
    end;
  if ri < molbufindex then
    begin
      inc(ri);
      rline := molbuf^[ri];
    end;
  tmpstr := copy(rline,1,5);
  val(tmpstr,n_atoms,code);
  tmpstr := copy(rline,7,5);
  val(tmpstr,n_bonds,code);
  try
    getmem(atom,n_atoms*sizeof(atom_rec));
    getmem(bond,n_bonds*sizeof(bond_rec));
    getmem(ring,sizeof(ringlist));
  except
    on e:Eoutofmemory do
      begin
        mod_wrt_ln('Not enough memory');
        halt(4);
      end;
  end;
  n_heavyatoms := 0;
  n_heavybonds := 0;
  while ((ri < molbufindex) and (pos('@<TRIPOS>ATOM',rline)=0)) do
    begin
      inc(ri);
      rline := molbuf^[ri];
    end;
  for n := 1 to n_atoms do
  begin
    with atom^[n] do
      begin
        formal_charge  := 0;
        real_charge    := 0;
        Hexp           := 0;
        Htot           := 0;
        neighbor_count := 0;
        ring_count     := 0;
        arom           := FALSE;
      end;
    if (ri < molbufindex) then
      begin
        inc(ri);
        rline := molbuf^[ri];
      end;
    sybatomtype := copy(rline,48,5);
    elemstr  := get_sybelement(sybatomtype);
    newatomtype := convert_sybtype(sybatomtype);
    xstr := copy(rline,18,9);
    ystr := copy(rline,28,9);
    zstr := copy(rline,38,9);
    chgstr := copy(rline,70,9);
    val(xstr,xval,code);
    val(ystr,yval,code);
    val(zstr,zval,code);
    val(chgstr,chgval,code);
    with atom^[n] do
      begin
        element := elemstr;
        atype := newatomtype;
        x := xval; y := yval; z := zval; real_charge := chgval;
      end;
    if is_heavyatom(n) then inc(n_heavyatoms);
  end;
  while ((ri < molbufindex) and (pos('@<TRIPOS>BOND',rline)=0)) do
    begin
      inc(ri);
      rline := molbuf^[ri];
    end;
  for n := 1 to n_bonds do
  begin
    if (ri < molbufindex) then
      begin
        inc(ri);
        rline := molbuf^[ri];
      end;
    a1str := copy(rline,9,3);
    a2str := copy(rline,14,3);
    val(a1str,a1val,code);
    if code <> 0 then mod_wrt_ln(rline, #7);
    val(a2str,a2val,code);
    if code <> 0 then mod_wrt_ln(rline,#7);
    with bond^[n] do
      begin
        a1 := a1val; a2 := a2val;
        if rline[19] = '1' then btype := 'S';
        if rline[19] = '2' then btype := 'D';
        if rline[19] = '3' then btype := 'T';
        if rline[19] = 'a' then btype := 'A';
        ring_count := 0; arom := false;
      end;
    if is_heavyatom(a1val) and is_heavyatom(a2val) then inc(n_heavybonds);
  end;
  fillchar(ring^,sizeof(ringlist),0);
  li := ri + 1;
end;


procedure read_charges(chgstring:string);
var
  a_id, a_chg : integer;
  n_chrg : integer;
  // typical example: a molecule with 2 kations + 1 anion
  // M  CHG  3   8   1  10   1  11  -1
begin
  if (pos('M  CHG',chgstring)>0) then
    begin
      delete(chgstring,1,6);
      left_trim(chgstring);
      n_chrg := left_int(chgstring);
      //debug
      //if (n_chrg = 0) then mod_wrt_ln('strange... M  CHG present, but no charges found');
      while (length(chgstring) > 0) do
        begin
          a_id  := left_int(chgstring);
          a_chg := left_int(chgstring);
          if (a_id <> 0) and (a_chg <> 0) then atom^[a_id].formal_charge := a_chg;
        end;
    end;
end;


procedure read_MDLmolfile(mfilename:string);  // reads MDL mol files
var
  n, code : integer;
  rline, tmpstr : string;
  xstr, ystr, zstr, chgstr : string;
  xval, yval, zval, chgval : single;
  a1str, a2str, elemstr : string;
  a1val, a2val : integer;
  ri : integer;
  sepcount : integer;
begin
  if n_atoms > 0 then zap_molecule;
  cm_mdlmolfile := false;
  rline := '';
  ri := li;
  molname := molbuf^[ri];            // line 1
  if ri < molbufindex then inc(ri);  // line 2
  rline   := molbuf^[ri];
  if pos('CheckMol',rline)=3 then
    begin
      cm_mdlmolfile := true;
      found_arominfo := true;
    end;
  if ri < molbufindex then inc(ri);  // line 3
  rline   := molbuf^[ri];
  molcomment := rline;
  if ri < molbufindex then inc(ri);  // line 4
  rline := molbuf^[ri];
  tmpstr := copy(rline,1,3);
  val(tmpstr,n_atoms,code);
  tmpstr := copy(rline,4,3);
  val(tmpstr,n_bonds,code);
  tmpstr := copy(rline,10,3);   // if it is a CheckMol-tweaked molfile, this is the number of rings
  n_cmrings := 0;
  val(tmpstr,n_cmrings,code);
  if code <> 0 then n_cmrings := 0;
  try
    getmem(atom,n_atoms*sizeof(atom_rec));
    getmem(bond,n_bonds*sizeof(bond_rec));
    getmem(ring,sizeof(ringlist));
  except
    on e:Eoutofmemory do
      begin
        mod_wrt_ln('Not enough memory');
        close(molfile);
        halt(4);
        exit;
      end;
  end;
  n_heavyatoms := 0;
  n_heavybonds := 0;
  for n := 1 to n_atoms do
    begin
      with atom^[n] do
        begin
          formal_charge  := 0;
          real_charge    := 0;
          Hexp           := 0;
          Htot           := 0;
          neighbor_count := 0;
          ring_count     := 0;
          arom           := FALSE;
        end;
      inc(ri);
      rline := molbuf^[ri];
      atomtype := copy(rline,32,3);
      elemstr  := get_MDLelement(atomtype);
      newatomtype := convert_MDLtype(atomtype);
      xstr := copy(rline,2,9);
      ystr := copy(rline,12,9);
      zstr := copy(rline,22,9);
      chgstr := '0';
      val(xstr,xval,code);
      val(ystr,yval,code);
      val(zstr,zval,code);
      val(chgstr,chgval,code);
      with atom^[n] do
        begin
          element := elemstr;
          if (elemstr = 'A ') or (elemstr = 'Q ') then found_querymol := true;
          atype := newatomtype;
          x := xval; y := yval; z := zval; real_charge := chgval;
          // read aromaticity flag from CheckMol-tweaked MDL molfile
          if (length(rline) > 37) and (rline[38] = '0') then
            begin
              arom := true;
              found_arominfo := true;
            end;
        end;
      if is_heavyatom(n) then inc(n_heavyatoms);
    end;
  for n := 1 to n_bonds do
    begin
      inc(ri);
      rline := molbuf^[ri];
      a1str := copy(rline,1,3);
      a2str := copy(rline,4,3);
      val(a1str,a1val,code);
      if code <> 0 then beep;
      val(a2str,a2val,code);
      if code <> 0 then beep;
      with bond^[n] do
        begin
          a1 := a1val; a2 := a2val;
          if rline[9] = '1' then btype := 'S';  // single
          if rline[9] = '2' then btype := 'D';  // double
          if rline[9] = '3' then btype := 'T';  // triple
          if rline[9] = '4' then btype := 'A';  // aromatic
          if rline[9] = '5' then btype := 'l';  // single or double
          if rline[9] = '6' then btype := 's';  // single or aromatic
          if rline[9] = '7' then btype := 'd';  // double or aromatic
          if rline[9] = '8' then btype := 'a';  // any
          if pos(btype,'lsda') > 0 then found_querymol := true;
          arom := false;
          // read aromaticity flag from CheckMol-tweaked MDL molfile
          if (btype = 'A') or (rline[8] = '0') then
            begin
              arom := true;
              if (rline[8] = '0') then found_arominfo := true;
            end;
          ring_count := 0;
        end;
      if is_heavyatom(a1val) and is_heavyatom(a2val) then inc(n_heavybonds);
    end;
  sepcount := 0;
  while (ri < molbufindex) and (sepcount < 1) do
    begin
      inc(ri);
      rline := molbuf^[ri];
      if (pos('M  CHG',rline) > 0) then read_charges(rline);
      if (pos('$$$$',rline)>0) then
        begin
          inc(sepcount);
          if (molbufindex > (ri + 2)) then mol_in_queue := true;  // we assume this is an SDF file
        end;
    end;
  fillchar(ring^,sizeof(ringlist),0);
  li := ri + 1;
end;


procedure lblank(cols:integer; var nstr:string);
// inserts leading blanks up to a given number of total characters
begin
  if length(nstr) >= cols then exit;
  while length(nstr) < cols do nstr := ' '+nstr;
end;


procedure mod_wrt_noln_MDLmolfile;
var
  i : integer;
  tmpstr : string;
  wline : string;
  a_chg : integer;
begin
  if length(molname)>80 then molname := copy(molname,1,80);
  mod_wrt_ln(molname);
  mod_wrt_ln('  CheckMol  ');
  mod_wrt_ln(molcomment);
  wline := ''; tmpstr := '';
  str(n_atoms:1,tmpstr); lblank(3,tmpstr);
  wline := wline+tmpstr; tmpstr := '';   // first 3 digits: number of atoms
  str(n_bonds:1,tmpstr); lblank(3,tmpstr);
  wline := wline+tmpstr; tmpstr := '';   // next 3 digits: number of bonds
  tmpstr := '  0';
  wline := wline + tmpstr; tmpstr := ''; // next 3 digits: number of atom lists (not used by us)
  str(n_rings:1,tmpstr); lblank(3,tmpstr);
  wline := wline+tmpstr; tmpstr := '';   // officially "obsolete", we use it for the number of rings
  wline := wline + '                  999 V2000';
  mod_wrt_ln(wline);
  for i := 1 to n_atoms do
    begin
      wline := '';
      str(atom^[i].X:1:4,tmpstr); lblank(10,tmpstr); wline := wline + tmpstr;
      str(atom^[i].Y:1:4,tmpstr); lblank(10,tmpstr); wline := wline + tmpstr;
      str(atom^[i].Z:1:4,tmpstr); lblank(10,tmpstr); wline := wline + tmpstr;
      tmpstr := atom^[i].element;
      tmpstr := lowercase(tmpstr);
      tmpstr[1] := upcase(tmpstr[1]);
      //wline := wline + ' '+atom^[i].element+' ';
      wline := wline + ' '+tmpstr+' ';
      wline := wline + ' 0';   // mass difference (isotopes)
      // now we code aromaticity into the old-style charge column (charges are now in the M  CHG line)
      if atom^[i].arom then tmpstr := ' 00' else tmpstr := '  0'; wline := wline + tmpstr;
      wline := wline + '  0  0  0  0  0  0  0  0  0  0';
      mod_wrt_ln(wline);
    end;
  for i := 1 to n_bonds do
    begin
      wline := '';
      str(bond^[i].a1:1,tmpstr); lblank(3,tmpstr); wline := wline + tmpstr;
      str(bond^[i].a2:1,tmpstr); lblank(3,tmpstr); wline := wline + tmpstr;
      if bond^[i].btype = 'S' then tmpstr := '  1';
      if bond^[i].btype = 'D' then tmpstr := '  2';
      if bond^[i].btype = 'T' then tmpstr := '  3';
      if bond^[i].btype = 'A' then tmpstr := '  4';
      if bond^[i].btype = 'l' then tmpstr := '  5';
      if bond^[i].btype = 's' then tmpstr := '  6';
      if bond^[i].btype = 'd' then tmpstr := '  7';
      if bond^[i].btype = 'a' then tmpstr := '  8';
      // now encode our own aromaticity information
      if bond^[i].arom then tmpstr[2] := '0';
      wline := wline + tmpstr+ '  0  0  0  0';
      mod_wrt_ln(wline);
    end;
  for i := 1 to n_atoms do
    begin
      a_chg := atom^[i].formal_charge;
      if a_chg <> 0 then
        begin
          wline := 'M  CHG  1 ';
          str(i:1,tmpstr); lblank(3,tmpstr); wline := wline + tmpstr +' ';
          str(a_chg:1,tmpstr); lblank(3,tmpstr); wline := wline + tmpstr;
          mod_wrt_ln(wline);
        end;
    end;
  mod_wrt_ln('M  END');
end;


// chemical processing functions & procedures


function nvalences(a_el:str2):integer;
// preliminary version; should be extended to element/atomtype
var
  res : integer;
begin
  res := 1;
  if a_el = 'H ' then res := 1;
  if a_el = 'C ' then res := 4;
  if a_el = 'N ' then res := 3;
  if a_el = 'O ' then res := 2;
  if a_el = 'S ' then res := 2;
  if a_el = 'SE' then res := 2;
  if a_el = 'TE' then res := 2;
  if a_el = 'P ' then res := 3;
  if a_el = 'F ' then res := 1;
  if a_el = 'CL' then res := 1;
  if a_el = 'BR' then res := 1;
  if a_el = 'I ' then res := 1;
  if a_el = 'B ' then res := 3;
  if a_el = 'LI' then res := 1;
  if a_el = 'NA' then res := 1;
  if a_el = 'K ' then res := 1;
  if a_el = 'CA' then res := 2;
  if a_el = 'SR' then res := 2;
  if a_el = 'MG' then res := 2;
  if a_el = 'FE' then res := 3;
  if a_el = 'MN' then res := 2;
  if a_el = 'HG' then res := 2;
  if a_el = 'SI' then res := 4;
  if a_el = 'SN' then res := 4;
  if a_el = 'ZN' then res := 2;
  if a_el = 'CU' then res := 2;
  if a_el = 'A ' then res := 4;
  if a_el = 'Q ' then res := 4;

  nvalences := res;
end;


procedure count_neighbors;
// counts heavy-atom neighbors and explicit hydrogens
var
  i : integer;
begin
  if (n_atoms < 1) or (n_bonds < 1) then exit;
  for i := 1 to n_bonds do
    begin
      if is_heavyatom(bond^[i].a1) then inc(atom^[(bond^[i].a2)].neighbor_count);
      if is_heavyatom(bond^[i].a2) then inc(atom^[(bond^[i].a1)].neighbor_count);
      if (atom^[(bond^[i].a1)].element = 'H ') then inc(atom^[(bond^[i].a2)].Hexp);
      if (atom^[(bond^[i].a2)].element = 'H ') then inc(atom^[(bond^[i].a1)].Hexp);
    end;
end;


function get_neighbors(id:integer):neighbor_rec;
var
  i : integer;
  nb_tmp : neighbor_rec;
  nb_count : integer;
begin
  fillchar(nb_tmp,sizeof(neighbor_rec),0);
  nb_count := 0;
  for i := 1 to n_bonds do
    begin
      if ((bond^[i].a1 = id) and (nb_count < 8)) and (is_heavyatom(bond^[i].a2)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := bond^[i].a2;
        end;
      if ((bond^[i].a2 = id) and (nb_count < 8)) and (is_heavyatom(bond^[i].a1)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := bond^[i].a1;
        end;
    end;
  get_neighbors := nb_tmp;
  // debug
  //  if nb_count <> atom^[id].neighbor_count then
  //    begin
  //      mod_wrt_ln('Error! atom^[',id,'].neighborcount = ',atom^[id].neighbor_count,', nb_count = ',nb_count);
  //      mod_wrt_ln;
  //    end;
  // end debug
end;


function get_ndl_neighbors(id:integer):neighbor_rec;
var
  i : integer;
  nb_tmp : neighbor_rec;
  nb_count : integer;
begin
  fillchar(nb_tmp,sizeof(neighbor_rec),0);
  nb_count := 0;
  for i := 1 to ndl_n_bonds do
    begin
      if ((ndl_bond^[i].a1 = id) and (nb_count < 8)) and (is_ndl_heavyatom(ndl_bond^[i].a2)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := ndl_bond^[i].a2;
        end;
      if ((ndl_bond^[i].a2 = id) and (nb_count < 8)) and (is_ndl_heavyatom(ndl_bond^[i].a1)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := ndl_bond^[i].a1;
        end;
    end;
  get_ndl_neighbors := nb_tmp;
  // debug
  //  if nb_count <> ndl_atom^[id].neighbor_count then
  //    begin
  //      mod_wrt_ln('Error! ndl_atom^[',id,'].neighborcount = ',ndl_atom^[id].neighbor_count,', nb_count = ',nb_count);
  //      mod_wrt_ln;
  //    end;
  // end debug
end;


function get_nextneighbors(id:integer;prev_id:integer):neighbor_rec;
var
  i : integer;
  nb_tmp : neighbor_rec;
  nb_count : integer;
begin
  // gets all neighbors except prev_id (usually the atom where we came from
  fillchar(nb_tmp,sizeof(neighbor_rec),0);
  nb_count := 0;
  for i := 1 to n_bonds do
    begin
      if ((bond^[i].a1 = id) and (bond^[i].a2 <> prev_id) and (nb_count < 8)) and (is_heavyatom(bond^[i].a2)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := bond^[i].a2;
        end;
      if ((bond^[i].a2 = id) and (bond^[i].a1 <> prev_id) and (nb_count < 8)) and (is_heavyatom(bond^[i].a1)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := bond^[i].a1;
        end;
    end;
  get_nextneighbors := nb_tmp;
  // debug
  //  if nb_count <> atom^[id].neighbor_count then
  //    begin
  //      mod_wrt_ln('Error! atom^[',id,'].neighborcount = ',atom^[id].neighbor_count,', nb_count = ',nb_count);
  //      mod_wrt_ln;
  //    end;
  // end debug
end;


function get_ndl_nextneighbors(id:integer;prev_id:integer):neighbor_rec;
var
  i : integer;
  nb_tmp : neighbor_rec;
  nb_count : integer;
begin
  // gets all neighbors except prev_id (usually the atom where we came from
  fillchar(nb_tmp,sizeof(neighbor_rec),0);
  nb_count := 0;
  for i := 1 to ndl_n_bonds do
    begin
      if ((ndl_bond^[i].a1 = id) and (ndl_bond^[i].a2 <> prev_id) and (nb_count < 8)) and (is_ndl_heavyatom(ndl_bond^[i].a2)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := ndl_bond^[i].a2;
        end;
      if ((ndl_bond^[i].a2 = id) and (ndl_bond^[i].a1 <> prev_id) and (nb_count < 8)) and (is_ndl_heavyatom(ndl_bond^[i].a1)) then
        begin
          inc(nb_count);
          nb_tmp[nb_count] := ndl_bond^[i].a1;
        end;
    end;
  get_ndl_nextneighbors := nb_tmp;
  // debug
  //  if nb_count <> atom^[id].neighbor_count then
  //    begin
  //      mod_wrt_ln('Error! atom^[',id,'].neighborcount = ',atom^[id].neighbor_count,', nb_count = ',nb_count);
  //      mod_wrt_ln;
  //    end;
  // end debug
end;


function path_pos(id:integer;a_path:ringpath_type):integer;
var
  i, pp : integer;
begin
  pp := 0;
  for i := max_ringsize downto 1 do
    begin
      if (a_path[i] = id) then pp := i;
    end;
  path_pos := pp;
end;


function path_length(a_path:ringpath_type):integer;
begin
  if a_path[max_ringsize] <> 0 then path_length := max_ringsize else
    begin
      path_length := path_pos(0,a_path)-1;
    end;
end;


function matchpath_pos(id:integer;a_path:matchpath_type):integer;
var
  i, pp : integer;
begin
  pp := 0;
  for i := max_matchpath_length downto 1 do
    begin
      if (a_path[i] = id) then pp := i;
    end;
  matchpath_pos := pp;
end;


function matchpath_length(a_path:matchpath_type):integer;
begin
  if a_path[max_matchpath_length] <> 0 then matchpath_length := max_matchpath_length else
    begin
      matchpath_length := matchpath_pos(0,a_path)-1;
    end;
end;


function get_bond(ba1,ba2:integer):integer;
var
  i, b_id : integer;
begin
  b_id := 0;
  if n_bonds > 0 then begin
    for i := 1 to n_bonds do
      begin
        if ((bond^[i].a1 = ba1) and (bond^[i].a2 = ba2)) or
           ((bond^[i].a1 = ba2) and (bond^[i].a2 = ba1)) then
           b_id := i;
      end;
  end;
  get_bond := b_id;
end;


function get_ndl_bond(ba1,ba2:integer):integer;
var
  i, b_id : integer;
begin
  b_id := 0;
  if ndl_n_bonds > 0 then begin
    for i := 1 to ndl_n_bonds do
      begin
        if ((ndl_bond^[i].a1 = ba1) and (ndl_bond^[i].a2 = ba2)) or
           ((ndl_bond^[i].a1 = ba2) and (ndl_bond^[i].a2 = ba1)) then
           b_id := i;
      end;
  end;
  get_ndl_bond := b_id;
end;


procedure order_ringpath(var r_path:ringpath_type);
// order should be: array starts with atom of lowest number, followed by neighbor atom with lower number
var
  i, pl : integer;
  a_ref, a_left, a_right, a_tmp : integer;
begin
  pl := path_length(r_path);
  if (pl < 3) then exit;
  a_ref := n_atoms;  // start with highest possible value for an atom number
  for i := 1 to pl do
    begin
      if r_path[i] < a_ref then a_ref := r_path[i];  // find the minimum value ==> reference atom
    end;
  if a_ref < 1 then exit;  // just to be sure
  if path_pos(a_ref,r_path) < pl then a_right := r_path[(path_pos(a_ref,r_path)+1)] else a_right := r_path[1];
  if path_pos(a_ref,r_path) > 1 then a_left := r_path[(path_pos(a_ref,r_path)-1)] else a_left := r_path[pl];
  if a_right = a_left then exit;  // should never happen
  if a_right < a_left then
    begin  // correct ring numbering direction, only shift of the reference atom to the left end required
      while path_pos(a_ref,r_path) > 1 do
        begin
          a_tmp := r_path[1];
          for i := 1 to (pl - 1) do r_path[i] := r_path[(i+1)];
          r_path[pl] := a_tmp;
        end;
    end else
    begin  // wrong ring numbering direction, two steps required
      while path_pos(a_ref,r_path) < pl do
        begin  // step one: create "mirrored" ring path with reference atom at right end
          a_tmp := r_path[pl];
          for i := pl downto 2 do r_path[i] := r_path[(i-1)];
          r_path[1] := a_tmp;
        end;
      for i := 1 to (pl div 2) do
        begin  // one more mirroring
          a_tmp := r_path[i];
          r_path[i] := r_path[(pl+1)-i];
          r_path[(pl+1)-i] := a_tmp;
        end;
    end;
end;


function ringcompare(rp1,rp2:ringpath_type):integer;
var
  i, j, rc, rs1, rs2 : integer;
  n_common, max_cra : integer;
begin
  rc := 0;
  n_common := 0;
  rs1 := path_length(rp1);
  rs2 := path_length(rp2);
  if rs1 < rs2 then max_cra := rs1 else max_cra := rs2;
  for i := 1 to rs1 do
    for j := 1 to rs2 do
      if rp1[i] = rp2[j] then inc(n_common);
  if (rs1 = rs2) and (n_common = max_cra) then rc := 0 else
    begin
      if n_common = 0 then inc(rc,8);
      if n_common < max_cra then inc(rc,4) else
        begin
          if rs1 < rs2 then inc(rc,1) else inc(rc,2);
        end;
    end;
  ringcompare := rc;
end;


function rc_identical(rc_int:integer):boolean;
begin
  if rc_int = 0 then rc_identical := true else rc_identical := false;
end;


function rc_1in2(rc_int:integer):boolean;
begin
  if odd(rc_int) then rc_1in2 := true else rc_1in2 := false;
end;


function rc_2in1(rc_int:integer):boolean;
begin
  rc_int := rc_int div 2;
  if odd(rc_int) then rc_2in1 := true else rc_2in1 := false;
end;


function rc_different(rc_int:integer):boolean;
begin
  rc_int := rc_int div 4;
  if odd(rc_int) then rc_different := true else rc_different := false;
end;


function rc_independent(rc_int:integer):boolean;
begin
  rc_int := rc_int div 8;
  if odd(rc_int) then rc_independent := true else rc_independent := false;
end;


function is_newring(n_path:ringpath_type):boolean;
var
  i, j : integer;
  nr, same_ring : boolean;
  tmp_path : ringpath_type;
  rc_result : integer;
begin
  nr := true;
  if n_rings > 0 then
    begin
      case ringsearch_mode of
        rs_sar  : begin
                    for i := 1 to n_rings do
                      begin
                        same_ring := true;
                        for j := 1 to max_ringsize do
                          if (ring^[i,j] <> n_path[j]) then same_ring := false;
                        if same_ring then nr := false;
                      end;
                  end;
        rs_ssr  : begin
                    for i := 1 to n_rings do
                      begin
                        for j := 1 to max_ringsize do tmp_path[j] := ring^[i,j];
                        rc_result := ringcompare(n_path,tmp_path);
                        if rc_identical(rc_result) then nr := false;
                        if rc_1in2(rc_result) then
                          begin
                            // exchange existing ring by smaller one
                            for j := 1 to max_ringsize do ring^[i,j] := n_path[j];
                            nr := false;
                            debugoutput('replacing ring '+inttostr(i)+' by smaller one (ringsize: '+inttostr(path_length(n_path))+')');
                          end;
                        if rc_2in1(rc_result) then
                          begin
                            // new ring contains existing one, but is larger ==> discard
                            nr := false;
                          end;
                      end;
                  end;
      end;  // case
    end;
  is_newring := nr;
end;


procedure add_ring(n_path:ringpath_type);
var
  i, pl : integer;
  a1, a2 : integer;
begin
  pl := path_length(n_path);
  if pl < 1 then exit;
  if n_rings < max_rings then
    begin
      inc(n_rings);
      debugoutput('adding ring: '+inttostr(n_rings));
      a2 := n_path[pl];
      for i := 1 to pl do
        begin
          ring^[n_rings,i] := n_path[i];
          inc(atom^[(n_path[i])].ring_count);
          a1 := n_path[i];
          //inc(bond^[(get_bond(a1,a2))].ring_count);
          a2 := a1;
        end;
    end else debugoutput('max_rings exceeded!');
end;


function is_ringpath(s_path:ringpath_type):boolean;
var
  i, j : integer;
  nb : neighbor_rec;
  rp, new_atom : boolean;
  a_last, pl : integer;
  l_path : ringpath_type;
begin
  rp := false;
  new_atom := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  fillchar(l_path,sizeof(ringpath_type),0);
  pl := path_length(s_path);
  if pl < 1 then begin debugoutput('Oops! Got zero-length s_path!'); exit; end;
  for i := 1 to pl do
    begin
      l_path[i] := s_path[i];
    end;
  // check if ring is already closed
  if (pl > 2) and (l_path[pl] = l_path[1]) then
    begin
      l_path[pl] := 0;  // remove last entry (redundant!)
      order_ringpath(l_path);
      // debug
      // mod_wrt_noln('ordered: '); for j := 1 to pl do mod_wrt_noln(l_path[j],' '); mod_wrt_ln;
      // end debug
      if is_newring(l_path) then
        begin
          if (n_rings < max_rings) then add_ring(l_path) else
            begin
              debugoutput('maximum number of rings exceeded!');
              is_ringpath := false;
              exit;
            end;
        end;
      rp := true;
      is_ringpath := true;
      exit;
    end;
  // any other case: ring is not (yet) closed
  a_last := l_path[pl];
  nb := get_neighbors(a_last);
  if atom^[a_last].neighbor_count > 1 then
    begin
      if ((rp = false) and (n_rings < max_rings)) then   // added in v0.2: check if max_rings is reached
        begin  // if ring is not closed, continue searching
          for i := 1 to atom^[a_last].neighbor_count do
            begin
              new_atom := true;
              for j := 2 to pl do if nb[i] = l_path[j] then new_atom := false;
              // added in v0.1a: check if max_rings not yet reached
              // added in v0.2:  limit ring size to max_vringsize instead of max_ringsize
              if (new_atom) and (pl < max_vringsize) and (n_rings < max_rings) then
                begin
                  l_path[(pl+1)] := nb[i];
                  if (pl < max_ringsize-1) then l_path[pl+2] := 0;  // just to be sure
                  if is_ringpath(l_path) then rp := true;
                end;
            end;
        end;
    end;  // else mod_wrt_ln('Atom ',a_last,' is a dead end!');
  is_ringpath := rp;
end;


function is_ringbond(b_id:integer):boolean;
var
  i : integer;
  ra1, ra2 : integer;
  nb : neighbor_rec;
  search_path : ringpath_type;
  rb : boolean;
begin
  rb := false;
  ra1 := bond^[b_id].a1;
  ra2 := bond^[b_id].a2;
  fillchar(nb,sizeof(neighbor_rec),0);
  fillchar(search_path,sizeof(ringpath_type),0);
  nb := get_neighbors(ra2);
  if (atom^[ra2].neighbor_count > 1) and (atom^[ra1].neighbor_count > 1) then
    begin
      search_path[1] := ra1;
      search_path[2] := ra2;
      for i := 1 to atom^[ra2].neighbor_count do
        begin
          if (nb[i] <> ra1) and (is_heavyatom(nb[i])) then
            begin
              search_path[3] := nb[i];
              if is_ringpath(search_path) then rb := true;
            end;
        end;
    end;
  is_ringbond := rb;
end;


procedure chk_ringbonds;
var
  i : integer;
  a1rc, a2rc : integer;
begin
  if n_bonds < 1 then exit;
  for i := 1 to n_bonds do
    begin
      a1rc := atom^[(bond^[i].a1)].ring_count;
      a2rc := atom^[(bond^[i].a2)].ring_count;
      if ((n_rings = 0) or ((a1rc < n_rings) and (a2rc < n_rings) )) then
        begin
          if is_ringbond(i) then
            begin
              //inc(bond^[i].ring_count);
            end;
        end;
    end;
end;


procedure update_ringcount;
var
  i, j, a1, a2, b, pl : integer;
begin
  if n_rings > 0 then
    begin
      for i := 1 to n_rings do
        begin
          pl := path_length(ring^[i]);
          a2 := ring^[i,pl];
          for j := 1 to pl do
            begin
              a1 := ring^[i,j];
              inc(atom^[a1].ring_count);
              b := get_bond(a1,a2);
              inc(bond^[b].ring_count);
              a2 := a1;
            end;
        end;
    end;
end;


function hetbond_count(a:integer):integer;
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  hbc : single;
begin
  hbc := 0;
  if (a > 0) and (a <= n_atoms) then
    begin
      fillchar(nb,sizeof(neighbor_rec),0);
      nb := get_neighbors(a);
      if atom^[a].neighbor_count > 0 then
        begin
          for i := 1 to atom^[a].neighbor_count do
            begin
              nb_el := atom^[(nb[i])].element;
              if (nb_el <> 'C ') and (nb_el <> 'A ') and (nb_el <> 'H ') and
                 (nb_el <> 'LP') and (nb_el <> 'DU') then
                begin
                  if (bond^[(get_bond(a,nb[i]))].btype = 'S') then hbc := hbc + 1;
                  if (bond^[(get_bond(a,nb[i]))].btype = 'A') then hbc := hbc + 1.5;
                  if (bond^[(get_bond(a,nb[i]))].btype = 'D') then hbc := hbc + 2;
                  if (bond^[(get_bond(a,nb[i]))].btype = 'T') then hbc := hbc + 3;
                end;
            end;
        end;
    end;
  //debug
  //if (atom^[a].element = 'N ') then
  //  begin
  //    mod_wrt_ln('checking hetbond_count for N:  hbc = ',hbc);
  //  end;
  //end debug
  hetbond_count := round(hbc);
end;


function hetatom_count(a:integer):integer;
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  hac : integer;
begin
  hac := 0;
  if (a > 0) and (a <= n_atoms) then
    begin
      fillchar(nb,sizeof(neighbor_rec),0);
      nb := get_neighbors(a);
      if atom^[a].neighbor_count > 0 then
        begin
          for i := 1 to atom^[a].neighbor_count do
            begin
              nb_el := atom^[(nb[i])].element;
              if (nb_el <> 'C ') and (nb_el <> 'H ') and
                 (nb_el <> 'LP') and (nb_el <> 'DU') then inc(hac);
            end;
        end;
    end;
  hetatom_count := hac;
end;


function ndl_hetbond_count(a:integer):integer;
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  hbc : single;
begin
  hbc := 0;
  if (a > 0) and (a <= n_atoms) then
    begin
      fillchar(nb,sizeof(neighbor_rec),0);
      nb := get_ndl_neighbors(a);
      if ndl_atom^[a].neighbor_count > 0 then
        begin
          for i := 1 to ndl_atom^[a].neighbor_count do
            begin
              nb_el := ndl_atom^[(nb[i])].element;
              if (nb_el <> 'C ') and (nb_el <> 'H ') and
                 (nb_el <> 'LP') and (nb_el <> 'DU') then
                begin
                  if (ndl_bond^[(get_ndl_bond(a,nb[i]))].btype = 'S') then hbc := hbc + 1;
                  if (ndl_bond^[(get_ndl_bond(a,nb[i]))].btype = 'A') then hbc := hbc + 1.5;
                  if (ndl_bond^[(get_ndl_bond(a,nb[i]))].btype = 'D') then hbc := hbc + 2;
                  if (ndl_bond^[(get_ndl_bond(a,nb[i]))].btype = 'T') then hbc := hbc + 3;
                end;
            end;
        end;
    end;
  ndl_hetbond_count := round(hbc);
end;


function ndl_hetatom_count(a:integer):integer;
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  hac : integer;
begin
  hac := 0;
  if (a > 0) and (a <= ndl_n_atoms) then
    begin
      fillchar(nb,sizeof(neighbor_rec),0);
      nb := get_ndl_neighbors(a);
      if ndl_atom^[a].neighbor_count > 0 then
        begin
          for i := 1 to ndl_atom^[a].neighbor_count do
            begin
              nb_el := ndl_atom^[(nb[i])].element;
              if (nb_el <> 'C ') and (nb_el <> 'A ') and (nb_el <> 'H ') and
                 (nb_el <> 'LP') and (nb_el <> 'DU') then inc(hac);
            end;
        end;
    end;
  ndl_hetatom_count := hac;
end;


function is_oxo_C(id:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             ((atom^[(nb[i])].element = 'O ') { or
              (atom^[(nb[i])].element = 'S ')  or
              (atom^[(nb[i])].element = 'SE') } ) then     // no N, amidines are different...
             r := true;
        end;
    end;
  is_oxo_C := r;
end;


function is_thioxo_C(id:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             ((atom^[(nb[i])].element = 'S ')  or
              (atom^[(nb[i])].element = 'SE')) then     // no N, amidines are different...
             r := true;
        end;
    end;
  is_thioxo_C := r;
end;


function is_imino_C(id:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             (atom^[(nb[i])].element = 'N ') then
             r := true;
        end;
    end;
  is_imino_C := r;
end;


function is_true_imino_C(id:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  nb_el : str2;
  a_n : integer;
begin
  r := false;
  a_n := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             (atom^[(nb[i])].element = 'N ')  then a_n := nb[i];
        end;
      if (a_n > 0) then
        begin
          r := true;
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(a_n);
          for i := 1 to atom^[a_n].neighbor_count do
            begin
              nb_el := atom^[(nb[i])].element;
              if (nb_el <> 'C ') and (nb_el <> 'H ') then r := false;
            end;  
        end;
    end;
  is_true_imino_C := r;
end;


function is_exocyclic_imino_C(id,r_id:integer):boolean;
var
  i,j  : integer;
  r    : boolean;
  nb   : neighbor_rec;
  testring : ringpath_type;
  ring_size : integer;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  fillchar(testring,sizeof(ringpath_type),0);
  for j := 1 to max_ringsize do if ring^[r_id,j] > 0 then testring[j] := ring^[r_id,j];
  ring_size := path_length(testring);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             (atom^[(nb[i])].element = 'N ') then
               begin
                 r := true;
                 for j := 1 to ring_size do
                   if nb[i] = ring^[r_id,j] then r := false;
               end;
        end;
    end;
  is_exocyclic_imino_C := r;
end;


function is_exocyclic_methylene_C(id,r_id:integer):boolean;
var
  i,j  : integer;
  r    : boolean;
  nb   : neighbor_rec;
  testring : ringpath_type;
  ring_size : integer;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  fillchar(testring,sizeof(ringpath_type),0);
  for j := 1 to max_ringsize do if ring^[r_id,j] > 0 then testring[j] := ring^[r_id,j];
  ring_size := path_length(testring);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             (atom^[(nb[i])].element = 'C ') then
               begin
                 r := true;
                 for j := 1 to ring_size do
                   if nb[i] = ring^[r_id,j] then r := false;
               end;
        end;
    end;
  is_exocyclic_methylene_C := r;
end;


function is_hydroxy(a_view,a_ref:integer):boolean;
var
  r  : boolean;
begin
  r := false;
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'O3 ') and (atom^[a_ref].neighbor_count = 1) then r := true;
    end;
  is_hydroxy := r;
end;


function is_sulfanyl(a_view,a_ref:integer):boolean;
var
  r  : boolean;
begin
  r := false;
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'S3 ') and (atom^[a_ref].neighbor_count = 1) then r := true;
    end;
  is_sulfanyl := r;
end;


function is_amino(a_view,a_ref:integer):boolean;
var
  r  : boolean;
begin
  r := false;
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if ((atom^[a_ref].atype = 'N3 ') or (atom^[a_ref].atype = 'N3+'))
          and (atom^[a_ref].neighbor_count = 1) then r := true;
    end;
  is_amino := r;
end;


function is_alkyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  nb_el : str2;
  het_count : integer;
begin
  r := false;
  het_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') and
     (atom^[a_ref].atype = 'C3 ') and (atom^[a_ref].arom = false) then
    begin
      nb := get_neighbors(a_ref);
      for i := 1 to atom^[a_ref].neighbor_count do
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el <> 'C ') and (nb_el <> 'H ') and
             (nb_el <> 'DU') and (nb_el <> 'LP') then inc(het_count)
        end;
      if het_count <= 2 then r := true;  // we consider (e.g.) alkoxyalkyl groups as alkyl
    end;
  is_alkyl := r;
end;


function is_true_alkyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  nb_el : str2;
  het_count : integer;
begin
  r := false;
  het_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') and
     (atom^[a_ref].atype = 'C3 ') and (atom^[a_ref].arom = false) then
    begin
      nb := get_neighbors(a_ref);
      for i := 1 to atom^[a_ref].neighbor_count do
        begin
          if (nb[i] <> a_view) then
            begin
              nb_el := atom^[(nb[i])].element;
              if (nb_el <> 'C ') and (nb_el <> 'H ') and
                 (nb_el <> 'DU') then inc(het_count)
            end;
        end;
      if het_count = 0 then r := true;  //
    end;
  is_true_alkyl := r;
end;


function is_aryl(a_view,a_ref:integer):boolean;
var
  r  : boolean;
begin
  r := false;
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') and
     (atom^[a_ref].element = 'C ') and (atom^[a_ref].arom = true) then r := true;
  is_aryl := r;
end;


function is_alkoxy(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'O3 ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_alkyl(a_ref,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_alkoxy := r;
end;


function is_siloxy(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'O3 ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if nb[i] <> a_view then
                begin
                  if (atom^[(nb[i])].element = 'SI') then r := true;
                end;
            end;
        end;
    end;
  is_siloxy := r;
end;


function is_true_alkoxy(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'O3 ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_true_alkyl(a_ref,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_true_alkoxy := r;
end;


function is_aryloxy(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'O3 ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_aryl(a_ref,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_aryloxy := r;
end;


function is_alkylsulfanyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'S3 ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_alkyl(a_ref,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_alkylsulfanyl := r;
end;


function is_true_alkylsulfanyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'S3 ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_true_alkyl(a_ref,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_true_alkylsulfanyl := r;
end;


function is_arylsulfanyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].atype = 'S3 ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_aryl(a_ref,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_arylsulfanyl := r;
end;


function is_alkylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  alkyl_count : integer;
begin
  r := false;
  alkyl_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_alkyl(a_ref,nb[i]) then inc(alkyl_count);
                end;
            end;
          if alkyl_count = 1 then  r := true  
        end;
    end;
  is_alkylamino := r;
end;


function is_dialkylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  alkyl_count : integer;
begin
  r := false;
  alkyl_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_alkyl(a_ref,nb[i]) then inc(alkyl_count);
                end;
            end;
          if alkyl_count = 2 then  r := true  
        end;
    end;
  is_dialkylamino := r;
end;


function is_arylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  aryl_count : integer;
begin
  r := false;
  aryl_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_aryl(a_ref,nb[i]) then inc(aryl_count);
                end;
            end;
          if aryl_count = 1 then  r := true  
        end;
    end;
  is_arylamino := r;
end;


function is_diarylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  aryl_count : integer;
begin
  r := false;
  aryl_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_aryl(a_ref,nb[i]) then inc(aryl_count);
                end;
            end;
          if aryl_count = 2 then  r := true
        end;
    end;
  is_diarylamino := r;
end;


function is_alkylarylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  alkyl_count : integer;
  aryl_count  : integer;
begin
  r := false;
  alkyl_count := 0;
  aryl_count  := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_alkyl(a_ref,nb[i]) then inc(alkyl_count);
                  if is_aryl(a_ref,nb[i]) then inc(aryl_count);
                end;
            end;
          if (alkyl_count = 1) and (aryl_count = 1) then  r := true  
        end;
    end;
  is_alkylarylamino := r;
end;


function is_subst_amino(a_view,a_ref:integer):boolean;
var
  r : boolean;
begin
  r := false;
  if (is_amino(a_view,a_ref)) or (is_alkylamino(a_view,a_ref)) or
     (is_arylamino(a_view,a_ref)) or (is_dialkylamino(a_view,a_ref)) or
     (is_alkylarylamino(a_view,a_ref)) or (is_diarylamino(a_view,a_ref)) then r := true;
  is_subst_amino := r;
end;


function is_true_alkylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  alkyl_count : integer;
begin
  r := false;
  alkyl_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if ((atom^[a_ref].atype = 'N3 ') or (atom^[a_ref].atype = 'N3+'))
         and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_true_alkyl(a_ref,nb[i]) then inc(alkyl_count);
                end;
            end;
          if alkyl_count = 1 then  r := true
        end;
    end;
  is_true_alkylamino := r;
end;


function is_true_dialkylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  alkyl_count : integer;
begin
  r := false;
  alkyl_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if ((atom^[a_ref].atype = 'N3 ') or (atom^[a_ref].atype = 'N3+'))
         and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_true_alkyl(a_ref,nb[i]) then inc(alkyl_count);
                end;
            end;
          if alkyl_count = 2 then  r := true  
        end;
    end;
  is_true_dialkylamino := r;
end;


function is_true_alkylarylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  alkyl_count : integer;
  aryl_count  : integer;
begin
  r := false;
  alkyl_count := 0;
  aryl_count  := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if ((atom^[a_ref].atype = 'N3 ') or (atom^[a_ref].atype = 'N3+'))
         and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_true_alkyl(a_ref,nb[i]) then inc(alkyl_count);
                  if is_aryl(a_ref,nb[i]) then inc(aryl_count);
                end;
            end;
          if (alkyl_count = 1) and (aryl_count = 1) then  r := true  
        end;
    end;
  is_true_alkylarylamino := r;
end;


function is_hydroxylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  oh_count : integer;
begin
  r := false;
  oh_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_hydroxy(a_ref,nb[i]) then inc(oh_count);
                end;
            end;
          if (oh_count = 1) then  r := true  
        end;
    end;
  is_hydroxylamino := r;
end;


function is_nitro(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  o_count : integer;
  bond_count : integer;
begin
  r := false;
  o_count := 0;
  bond_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if (atom^[(nb[i])].element = 'O ') then inc(o_count);
                  if (bond^[get_bond(a_ref,nb[i])].btype = 'S') then inc(bond_count);
                  if (bond^[get_bond(a_ref,nb[i])].btype = 'D') then inc(bond_count,2);
                end;
            end;
          if (o_count = 2) and (bond_count >= 3) then  r := true  
        end;
    end;
  is_nitro := r;
end;


function is_azido(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  bond_count : integer;
  n1, n2, n3 : integer;
begin
  r := false;
  bond_count := 0;
  n1 := 0; n2 := 0; n3 := 0;
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          n1 := a_ref;
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(n1);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if (atom^[(nb[i])].element = 'N ') then
                    begin
                      n2 := nb[i];
                      if (bond^[get_bond(n1,n2)].btype = 'S') then inc(bond_count);
                      if (bond^[get_bond(n1,n2)].btype = 'D') then inc(bond_count,2);
                      if (bond^[get_bond(n1,n2)].btype = 'T') then inc(bond_count,3);
                    end;
                end;
            end;
          if (n2 > 0) and (atom^[n2].neighbor_count = 2) then
            begin
              fillchar(nb,sizeof(neighbor_rec),0);
              nb := get_neighbors(n2);
              for i := 1 to 2 do
                begin
                  if (nb[i] <> n1) then
                    begin
                      if (atom^[(nb[i])].element = 'N ') then
                        begin
                          n3 := nb[i];
                          if (bond^[get_bond(n2,n3)].btype = 'S') then inc(bond_count);
                          if (bond^[get_bond(n2,n3)].btype = 'D') then inc(bond_count,2);
                          if (bond^[get_bond(n2,n3)].btype = 'T') then inc(bond_count,3);
                        end;
                    end;
                end;
            end;  
          if (n1 > 0) and (n2 > 0) and (n3 > 0) and (atom^[n3].neighbor_count = 1) and
             (bond_count > 3) then r := true  
        end;
    end;
  is_azido := r;
end;


function is_diazonium(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  bond_count : integer;
  chg_count : integer;
  n1, n2 : integer;
begin
  r := false;
  bond_count := 0;
  chg_count := 0;
  n1 := 0; n2 := 0;
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          n1 := a_ref;
          chg_count := atom^[n1].formal_charge;
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(n1);
          for i := 1 to 2 do
            begin
              if (nb[i] <> n1) then
                begin
                  if (atom^[(nb[i])].element = 'N ') then
                    begin
                      n2 := nb[i];
                      chg_count := chg_count + atom^[n2].formal_charge;                      
                      if (bond^[get_bond(n1,n2)].btype = 'S') then inc(bond_count);
                      if (bond^[get_bond(n1,n2)].btype = 'D') then inc(bond_count,2);
                      if (bond^[get_bond(n1,n2)].btype = 'T') then inc(bond_count,3);
                    end;
                end;
            end;
          if (n1 > 0) and (n2 > 0) and (atom^[n2].neighbor_count = 1) and
             (bond_count >= 2) and (chg_count > 0) then r := true
        end;
    end;
  is_diazonium := r;
end;


function is_hydroximino_C(id:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  a_het : integer;
begin
  r := false;
  a_het := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             (atom^[(nb[i])].element = 'N ') and
             (hetbond_count(nb[i]) = 3) then
             a_het := nb[i];;
        end;
      if (a_het > 0) then
        begin
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(a_het);
          if (atom^[a_het].element = 'N ') and (atom^[a_het].neighbor_count > 0) then
            begin
              for i := 1 to atom^[a_het].neighbor_count do
                begin
                  if is_hydroxy(a_het,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_hydroximino_C := r;
end;


function is_hydrazono_C(id:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  a_het : integer;
begin
  r := false;
  a_het := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (id < 1) or (id > n_atoms) then exit;
  nb := get_neighbors(id);
  if (atom^[id].element = 'C ') and (atom^[id].neighbor_count > 0) then
    begin
      for i := 1 to atom^[id].neighbor_count do
        begin
          if (bond^[get_bond(id,nb[i])].btype = 'D') and
             (atom^[(nb[i])].element = 'N ') { and
             (hetbond_count(nb[i]) = 3)  } then
             a_het := nb[i];;
        end;
      if (a_het > 0) then
        begin
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(a_het);
          if (atom^[a_het].element = 'N ') and (atom^[a_het].neighbor_count > 0) then
            begin
              for i := 1 to atom^[a_het].neighbor_count do
                begin
                  if (is_amino(a_het,nb[i])) or
                     (is_alkylamino(a_het,nb[i])) or
                     (is_alkylarylamino(a_het,nb[i])) or
                     (is_arylamino(a_het,nb[i])) or
                     (is_dialkylamino(a_het,nb[i])) or
                     (is_diarylamino(a_het,nb[i]))
                   then r := true;
                end;
            end;
        end;
    end;
  is_hydrazono_C := r;
end;


function is_alkoxycarbonyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_oxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_alkoxy(a_ref,nb[i]) then r := true;
                end;
            end;
        end;    
    end;
  is_alkoxycarbonyl := r;  
end;


function is_aryloxycarbonyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_oxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_aryloxy(a_ref,nb[i]) then r := true;
                end;
            end;
        end;
    end;
  is_aryloxycarbonyl := r;
end;


function is_carbamoyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_oxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if (atom^[(nb[i])].atype = 'N3 ') or 
                     (atom^[(nb[i])].atype = 'NAM') then r := true;
                end;
            end;
        end;    
    end;
  is_carbamoyl := r;  
end;


function is_alkoxythiocarbonyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_thioxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_alkoxy(a_ref,nb[i]) then r := true;
                end;
            end;
        end;    
    end;
  is_alkoxythiocarbonyl := r;  
end;


function is_aryloxythiocarbonyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_thioxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if is_aryloxy(a_ref,nb[i]) then r := true;
                end;
            end;
        end;    
    end;
  is_aryloxythiocarbonyl := r;  
end;


function is_thiocarbamoyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_thioxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if (atom^[(nb[i])].atype = 'N3 ') or
                     (atom^[(nb[i])].atype = 'NAM') then r := true;
                end;
            end;
        end;
    end;
  is_thiocarbamoyl := r;
end;


function is_alkanoyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_oxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if (is_alkyl(a_ref,nb[i])) then r := true;
                end;
            end;
        end;    
    end;
  is_alkanoyl := r;  
end;


function is_aroyl(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (is_oxo_c(a_ref)) and (atom^[a_ref].neighbor_count = 3) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 3 do
            begin
              if nb[i] <> a_view then
                begin
                  if (is_aryl(a_ref,nb[i])) then r := true;
                end;
            end;
        end;    
    end;
  is_aroyl := r;  
end;


function is_acyl(a_view,a_ref:integer):boolean;
var
  r  : boolean;
begin
  r := false;
  if (is_alkanoyl(a_view,a_ref)) or (is_aroyl(a_view,a_ref)) then r := true;
  is_acyl := r;
end;


function is_acylamino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  acyl_count : integer;
begin
  r := false;
  acyl_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count = 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if is_acyl(a_ref,nb[i]) then inc(acyl_count);
                end;
            end;
          if acyl_count = 1 then  r := true  
        end;
    end;
  is_acylamino := r;
end;


function is_hydrazino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  nr_count : integer;
begin
  r := false;
  nr_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count >= 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if (is_amino(a_ref,nb[i])) or
                     (is_subst_amino(a_ref,nb[i])) then inc(nr_count);
                end;
            end;
          if (nr_count = 1) then r := true  
        end;
    end;
  is_hydrazino := r;
end;


function is_subst_hydrazino(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  nr_count : integer;
begin
  r := false;
  nr_count := 0;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (is_heavyatom(a_view)) and (bond^[get_bond(a_view,a_ref)].btype = 'S') then
    begin
      if (atom^[a_ref].element = 'N ') and (atom^[a_ref].neighbor_count >= 2) then
        begin
          nb := get_neighbors(a_ref);
          for i := 1 to 2 do
            begin
              if (nb[i] <> a_view) then
                begin
                  if (atom^[(nb[i])].element= 'N ') then inc(nr_count);
                end;
            end;
          if (nr_count = 1) then r := true  
        end;
    end;
  is_subst_hydrazino := r;
end;


function is_cyano(a_view,a_ref:integer):boolean;
var
  r  : boolean;
begin
  r := false;
  if (atom^[a_view].atype = 'C1 ') and (bond^[get_bond(a_view,a_ref)].btype = 'T') and
     (atom^[a_ref].atype = 'N1 ') and (atom^[a_ref].neighbor_count = 1) then r := true;
  is_cyano := r;
end;


function is_cyano_c(a_ref:integer):boolean;
var
  i : integer;
  r  : boolean;
  nb : neighbor_rec;
  //nb_el : str2;
begin
  r := false;
  fillchar(nb,sizeof(neighbor_rec),0);
  if (atom^[a_ref].atype = 'C1 ') and (atom^[a_ref].neighbor_count > 0) then
    begin
      nb := get_neighbors(a_ref);
      for i := 1 to atom^[a_ref].neighbor_count do
        begin
          if (is_cyano(a_ref,nb[i])) then r := true;
        end;
    end;
  is_cyano_c := r;
end;


function is_nitrile(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
  nb_el : str2;
begin
  r := false;
  if is_cyano(a_view,a_ref) then
  begin
    if (atom^[a_view].neighbor_count = 1) and
       (atom^[a_view].formal_charge = 0) then r := true else   // HCN is also a nitrile!
      begin
        nb := get_neighbors(a_view);
        for i := 1 to 2 do
          begin
            if nb[i] <> a_ref then
              begin
                nb_el := atom^[(nb[i])].element;
                if (nb_el = 'C ') or (nb_el = 'H ') then r := true;
              end;
          end;
      end;
    end;
  is_nitrile := r;
end;


function is_isonitrile(a_view,a_ref:integer):boolean;   // only recognized with CN triple bond!
var
  r  : boolean;
begin
  r := false;
  if (atom^[a_view].atype = 'C1 ') and (bond^[get_bond(a_view,a_ref)].btype = 'T') and
     (atom^[a_ref].atype = 'N1 ') and (atom^[a_ref].neighbor_count = 2) and
     (atom^[a_view].neighbor_count = 1) then r := true;
  is_isonitrile := r;
end;


function is_cyanate(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  if is_cyano(a_view,a_ref) then
  begin
    if (atom^[a_view].neighbor_count = 2) then
      begin
        nb := get_neighbors(a_view);
        for i := 1 to 2 do
          begin
            if nb[i] <> a_ref then
              begin
                if (is_alkoxy(a_view,nb[i])) or
                   (is_aryloxy(a_view,nb[i])) then r := true;
              end;
          end;
      end;
    end;
  is_cyanate := r;
end;


function is_thiocyanate(a_view,a_ref:integer):boolean;
var
  i  : integer;
  r  : boolean;
  nb : neighbor_rec;
begin
  r := false;
  if is_cyano(a_view,a_ref) then
  begin
    if (atom^[a_view].neighbor_count = 2) then 
      begin
        nb := get_neighbors(a_view);
        for i := 1 to 2 do
          begin
            if nb[i] <> a_ref then
              begin
                if (is_alkylsulfanyl(a_view,nb[i])) or 
                   (is_arylsulfanyl(a_view,nb[i])) then r := true;
              end;
          end;
      end;
    end;
  is_thiocyanate := r;
end;


procedure update_Htotal;
var
  i, j, b_id : integer;
  nb : neighbor_rec;
  single_count, double_count, triple_count, arom_count : integer;
  total_bonds : integer;
  Htotal : integer;
begin
  if n_atoms < 1 then exit;
  fillchar(nb,sizeof(neighbor_rec),0);
  for i := 1 to n_atoms do
    begin
      single_count := 0;
      double_count := 0;
      triple_count := 0;
      arom_count   := 0;
      total_bonds  := 0;
      Htotal    := 0;
      nb := get_neighbors(i);
      if atom^[i].neighbor_count > 0 then
        begin  // count single, double, triple, and aromatic bonds to all neighbor atoms
          for j := 1 to atom^[i].neighbor_count do
            begin
              b_id := get_bond(i,nb[j]);
              if b_id > 0 then
                begin
                  if bond^[b_id].btype = 'S' then inc(single_count);
                  if bond^[b_id].btype = 'D' then inc(double_count);
                  if bond^[b_id].btype = 'T' then inc(triple_count);
                  if bond^[b_id].btype = 'A' then inc(arom_count);
                end;
            end;
          total_bonds := single_count + 2*double_count + 3*triple_count + trunc(1.5*arom_count);  
          // calculate number of total hydrogens per atom
          Htotal := nvalences(atom^[i].element) - total_bonds + atom^[i].formal_charge;
          if Htotal < 0 then Htotal := 0;  // e.g., N in nitro group
          atom^[i].Htot := Htotal;
        end;
    end;
end;


procedure update_atypes;
var
  i, j, b_id : integer;
  nb : neighbor_rec;
  single_count, double_count, triple_count, arom_count, acyl_count : integer;
  C_count, O_count : integer;
  total_bonds : integer;
  NdO_count : integer;
  NdC_count : integer;
  Htotal : integer;
begin
  if n_atoms < 1 then exit;
  fillchar(nb,sizeof(neighbor_rec),0);
  for i := 1 to n_atoms do
    begin
      single_count := 0;
      double_count := 0;
      triple_count := 0;
      arom_count   := 0;
      total_bonds  := 0;
      acyl_count   := 0;
      C_count      := 0;
      O_count      := 0;
      NdO_count := 0;
      NdC_count := 0;
      Htotal    := 0;
      nb := get_neighbors(i);
      if atom^[i].neighbor_count > 0 then
        begin  // count single, double, triple, and aromatic bonds to all neighbor atoms
          for j := 1 to atom^[i].neighbor_count do
            begin
              if (is_oxo_C(nb[j])) or (is_thioxo_C(nb[j])) then inc(acyl_count);
              if atom^[(nb[j])].element = 'C ' then inc(C_count);
              if atom^[(nb[j])].element = 'O ' then inc(O_count);
              b_id := get_bond(i,nb[j]);
              if b_id > 0 then
                begin
                  if bond^[b_id].btype = 'S' then inc(single_count);
                  if bond^[b_id].btype = 'D' then inc(double_count);
                  if bond^[b_id].btype = 'T' then inc(triple_count);
                  if bond^[b_id].btype = 'A' then inc(arom_count);
                  if ((atom^[i].element = 'N ') and (atom^[(nb[j])].element = 'O ')) or
                     ((atom^[i].element = 'O ') and (atom^[(nb[j])].element = 'N ')) then
                     begin
                       // check if it is an N-oxide drawn with a double bond ==> should be N3
                       if bond^[b_id].btype = 'D' then inc(NdO_count);
                     end;
                  if ((atom^[i].element = 'N ') and (atom^[(nb[j])].element = 'C ')) or
                     ((atom^[i].element = 'C ') and (atom^[(nb[j])].element = 'N ')) then
                     begin
                       if bond^[b_id].btype = 'D' then inc(NdC_count);
                     end;
                end;
            end;
          total_bonds := single_count + 2*double_count + 3*triple_count + trunc(1.5*arom_count);  
          // calculate number of total hydrogens per atom
          Htotal := nvalences(atom^[i].element) - total_bonds + atom^[i].formal_charge;
          if Htotal < 0 then Htotal := 0;  // e.g., N in nitro group
          atom^[i].Htot := Htotal;
          // refine atom types, based on bond types
          if atom^[i].element = 'C ' then
            begin
              if (arom_count > 1) then atom^[i].atype := 'CAR';
              if (triple_count = 1) or (double_count = 2) then atom^[i].atype := 'C1 ';
              if (double_count = 1) then atom^[i].atype := 'C2 ';
              if (triple_count = 0) and (double_count = 0) and (arom_count < 2) then atom^[i].atype := 'C3 ';
            end;  
          if atom^[i].element = 'O ' then
            begin
              if (double_count = 1) then atom^[i].atype := 'O2 ';
              if (double_count = 0) then atom^[i].atype := 'O3 ';
            end;
          if atom^[i].element = 'N ' then
            begin
              if total_bonds > 3 then
                begin
                  if O_count = 0 then
                    begin
                      if (single_count > 3) or
                        ((single_count = 2) and (double_count = 1) and (C_count >=2)) then
                        atom^[i].formal_charge := 1;
                    end else  // could be an N-oxide -> should be found elsewhere 
                    begin
                      // left empty, so far....
                    end;
                end;
              if (arom_count > 1) then atom^[i].atype := 'NAR';
              if (triple_count = 1) or (double_count = 2) then atom^[i].atype := 'N1 ';
              if (double_count = 1) then 
                begin
                  if NdC_count > 0 then atom^[i].atype := 'N2 ';
                  if (NdC_count = 0) and (NdO_count > 0) and
                     (C_count >= 2) then atom^[i].atype := 'N3 ';  // N-oxide is N3 except in hetarene etc.
                end;  
              if (triple_count = 0) and (double_count = 0) then 
                begin
                  if (atom^[i].formal_charge = 0) then 
                    begin
                      if (acyl_count = 0) then atom^[i].atype := 'N3 ';
                      if (acyl_count > 0) then atom^[i].atype := 'NAM';
                    end;  
                  if (atom^[i].formal_charge = 1) then atom^[i].atype := 'N3+';
                end;
            end;  
          if atom^[i].element = 'P ' then
            begin
              if (single_count > 4) then atom^[i].atype := 'P4 ';
              if (single_count <= 4) and (double_count = 0) then atom^[i].atype := 'P3 ';
              if (double_count = 2) then atom^[i].atype := 'P3D';
            end;
          if atom^[i].element = 'S ' then
            begin
              if (double_count = 1) and (single_count = 0) then atom^[i].atype := 'S2 ';
              if (double_count = 0) then atom^[i].atype := 'S3 ';
              if (double_count = 1) and (single_count > 0) then atom^[i].atype := 'SO ';
              if (double_count = 2) and (single_count > 0) then atom^[i].atype := 'SO2';
            end;
          // further atom types should go here
        end;
    end;
end;


procedure chk_arom;
var
  i, j, pi_count, ring_size : integer;
  testring : ringpath_type;
  a_ref, a_prev, a_next : integer;
  b_bk, b_fw : integer;
  bt_bk, bt_fw : char;
  conj_intr, ko : boolean;
begin
  if n_rings < 1 then exit;
  for i := 1 to n_rings do
    begin
      fillchar(testring,sizeof(ringpath_type),0);
      for j := 1 to max_ringsize do if ring^[i,j] > 0 then testring[j] := ring^[i,j];
      ring_size := path_length(testring);
      pi_count  := 0;
      conj_intr := false;
      ko        := false;
      a_prev    := testring[ring_size];
      for j := 1 to ring_size do
        begin
          a_ref := testring[j];
          if (j < ring_size) then a_next := testring[(j+1)] else a_next := testring[1];
          b_bk  := get_bond(a_prev,a_ref);
          b_fw  := get_bond(a_ref,a_next);
          bt_bk := bond^[b_bk].btype;
          bt_fw := bond^[b_fw].btype;
          if (bt_bk = 'S') and (bt_fw = 'S') then
            begin
              conj_intr := true;  // first, assume the worst case (interrupted conjugation)
              // conjugation can be restored by hetero atoms
              if (atom^[a_ref].atype = 'O3 ') or (atom^[a_ref].atype = 'S3 ') or
                 (atom^[a_ref].element = 'N ') or (atom^[a_ref].element = 'SE') then
                 begin
                   conj_intr := false;
                   inc(pi_count,2);  // lone pair adds for 2 pi electrons
                 end;
              // conjugation can be restored by a formal charge at a methylene group
              if (atom^[a_ref].element = 'C ') and (atom^[a_ref].formal_charge <> 0) then
                begin
                  conj_intr := false;
                  pi_count  := pi_count - atom^[a_ref].formal_charge;  // neg. charge increases pi_count!
                end;
              // conjugation can be restored by carbonyl groups etc.
              if (is_oxo_C(a_ref)) or (is_thioxo_C(a_ref)) or (is_exocyclic_imino_C(a_ref,i)) then
                begin
                  conj_intr := false;
                end;
              // conjugation can be restored by exocyclic C=C double bond,
              // adds 2 pi electrons to 5-membered rings, not to 7-membered rings
              if ((is_exocyclic_methylene_C(a_ref,i)) and odd(ring_size)) then
                begin
                  conj_intr := false;
                  if ((ring_size - 1) mod 4 = 0) then inc(pi_count,2);
                end;
              // if conjugation is still interrupted ==> knock-out
              if conj_intr then ko := true;
            end else
            begin  // any other combination of bond types adds 1 pi electron
              inc(pi_count);
            end;
          // last command:
          a_prev := a_ref;
          // debug
          // mod_wrt_noln('current atom: ',a_ref,'; previous atom: ',a_prev,'; bond types: ',bt_bk,', ',bt_fw);
          // mod_wrt_noln('  pi_count: ',pi_count);
          // if conj_intr then mod_wrt_noln(' conj. interrupted') else mod_wrt_noln(' conj. uninterrupted');
          // mod_wrt_ln;
        end;  // for j := 1 to ring_size
      // now we can draw our conclusion
      if not ((ko) or (odd(pi_count))) then
        begin  // apply Hueckel's rule
          if (abs(ring_size - pi_count) < 2) and ((pi_count - 2) mod 4 = 0) then
            begin
              // now mark _all_ bonds in the ring as aromatic
              a_prev := testring[ring_size];
              for j := 1 to ring_size do
                begin
                  a_ref := testring[j];
                  bond^[get_bond(a_prev,a_ref)].arom := true;
                  a_prev := a_ref;
                  // debug
                  //mod_wrt_ln('aromatic ring found!');
                  // end debug
                 end;
            end;
        end;
    end;  // inner "for" loop
  // finally, mark all involved atoms as aromatic
  for i := 1 to n_bonds do
    begin
      if bond^[i].arom then
        begin
          atom^[(bond^[i].a1)].arom := true;
          atom^[(bond^[i].a2)].arom := true;
        end;
    end;
end;


procedure mod_wrt_noln_mol;
var
  i, j : integer;
  testring : ringpath_type;
  ring_size : integer;
  aromatic : boolean;
  a_prev, a_ref : integer;
begin
{*  if progmode = pmCheckMol then
    mod_wrt_ln('Molecule name: ',molname)
  else
    mod_wrt_ln('Molecule name (haystack): ',molname);
  mod_wrt_ln('atoms: ',n_atoms,'  bonds: ',n_bonds,'  rings: ',n_rings);
  if n_atoms < 1 then exit;
  if n_bonds < 1 then exit;
  for i := 1 to n_atoms do
    begin
      if i <   10 then mod_wrt_noln(' ');
      if i <  100 then mod_wrt_noln(' ');
      if i < 1000 then mod_wrt_noln(' ');
      mod_wrt_noln(i,' ',atom^[i].element,' ',atom^[i].atype,' ',atom^[i].x:9:4,' ',atom^[i].y:9:4,' ');
      mod_wrt_noln(atom^[i].z:9:4);
      mod_wrt_noln('  (',atom^[i].neighbor_count,' heavy-atom neighbors)');
      if (atom^[i].formal_charge <> 0) then mod_wrt_noln('  charge: ',atom^[i].formal_charge);
      mod_wrt_ln;
    end;
  for i := 1 to n_bonds do
    begin
      if i <   10 then mod_wrt_noln(' ');
      if i <  100 then mod_wrt_noln(' ');
      if i < 1000 then mod_wrt_noln(' ');
      mod_wrt_noln(i,' ',bond^[i].a1,' ',bond^[i].a2,' ',bond^[i].btype);
      if bond^[i].ring_count > 0 then mod_wrt_noln(', contained in ',bond^[i].ring_count,' ring(s)');
      if bond^[i].arom then mod_wrt_noln(' (aromatic) ');
      mod_wrt_ln;
    end;
  if n_rings > 0 then
    begin
      for i := 1 to n_rings do
        begin
          aromatic := true;
          fillchar(testring,sizeof(ringpath_type),0);
          for j := 1 to max_ringsize do if ring^[i,j] > 0 then testring[j] := ring^[i,j];
          ring_size := path_length(testring);
          mod_wrt_noln('ring ',i,': ');
          a_prev := testring[ring_size];
          for j := 1 to ring_size do
            begin
              mod_wrt_noln(testring[j],' ');
              a_ref := testring[j];
              if (not bond^[get_bond(a_prev,a_ref)].arom) then aromatic := false;
              a_prev := a_ref;
            end;
          if aromatic then mod_wrt_noln(' (aromatic)');
          mod_wrt_ln;
        end;
    end;
*}end;


procedure mod_wrt_noln_needle_mol;
var
  i, j : integer;
  testring : ringpath_type;
  ring_size : integer;
  aromatic : boolean;
  a_prev, a_ref : integer;
begin
{*  mod_wrt_ln('Molecule name (needle): ',ndl_molname);
  mod_wrt_ln('atoms: ',ndl_n_atoms,'  bonds: ',ndl_n_bonds,'  rings: ',ndl_n_rings);
  if ndl_n_atoms < 1 then exit;
  if ndl_n_bonds < 1 then exit;
  for i := 1 to ndl_n_atoms do
    begin
      if i <   10 then mod_wrt_noln(' ');
      if i <  100 then mod_wrt_noln(' ');
      if i < 1000 then mod_wrt_noln(' ');
      mod_wrt_noln(i,' ',ndl_atom^[i].element,' ',ndl_atom^[i].atype,' ',ndl_atom^[i].x:9:4,' ',atom^[i].y:9:4,' ');
      mod_wrt_noln(ndl_atom^[i].z:9:4);
      mod_wrt_noln('  (',ndl_atom^[i].neighbor_count,' heavy-atom neighbors)');
      if (ndl_atom^[i].formal_charge <> 0) then mod_wrt_noln('  charge: ',ndl_atom^[i].formal_charge);
      mod_wrt_ln;
    end;
  for i := 1 to ndl_n_bonds do
    begin
      if i <   10 then mod_wrt_noln(' ');
      if i <  100 then mod_wrt_noln(' ');
      if i < 1000 then mod_wrt_noln(' ');
      mod_wrt_noln(i,' ',ndl_bond^[i].a1,' ',ndl_bond^[i].a2,' ',ndl_bond^[i].btype);
      if ndl_bond^[i].ring_count > 0 then mod_wrt_noln(', contained in ',ndl_bond^[i].ring_count,' ring(s)');
      if ndl_bond^[i].arom then mod_wrt_noln(' (aromatic) ');
      mod_wrt_ln;
    end;
  if ndl_n_rings > 0 then
    begin
      for i := 1 to ndl_n_rings do
        begin
          aromatic := true;
          fillchar(testring,sizeof(ringpath_type),0);
          for j := 1 to max_ringsize do if ndl_ring^[i,j] > 0 then testring[j] := ndl_ring^[i,j];
          ring_size := path_length(testring);
          mod_wrt_noln('ring ',i,': ');
          a_prev := testring[ring_size];
          for j := 1 to ring_size do
            begin
              mod_wrt_noln(testring[j],' ');
              a_ref := testring[j];
              if (not ndl_bond^[get_bond(a_prev,a_ref)].arom) then aromatic := false;
              a_prev := a_ref;
            end;
          if aromatic then mod_wrt_noln(' (aromatic)');
          mod_wrt_ln;
        end;
    end;
*}end;


procedure chk_so2_deriv(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  het_count, o_count, or_count, hal_count, n_count, c_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  if atom^[a_ref].atype <>'SO2' then exit;
  nb := get_neighbors(a_ref);
  het_count := 0; o_count := 0; or_count := 0; hal_count := 0; n_count := 0; c_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'S' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el <> 'C ') and (nb_el <> 'H ') and
             (nb_el <> 'DU') and (nb_el <> 'LP') then inc(het_count);
          if nb_el = 'O ' then
            begin
              inc(o_count);
              if is_alkoxy(a_ref,nb[i]) or is_aryloxy(a_ref,nb[i]) then inc(or_count);
            end;
          if nb_el = 'N ' then inc(n_count);
          if nb_el = 'C ' then inc(c_count);
          if (nb_el = 'F ') or (nb_el = 'CL') or (nb_el = 'BR') or (nb_el = 'I ') then inc(hal_count);
        end;
    end;
  if het_count = 2 then   // sulfuric acid derivative
    begin
      fg[fg_sulfuric_acid_deriv] := true;
      if (o_count = 2) then
        begin
          if (or_count = 0) then fg[fg_sulfuric_acid] := true;
          if (or_count = 1) then fg[fg_sulfuric_acid_monoester] := true;
          if (or_count = 2) then fg[fg_sulfuric_acid_diester] := true;
        end;
      if (o_count = 1) then
        begin
          if (or_count = 1) and (n_count = 1) then fg[fg_sulfuric_acid_amide_ester] := true;
          if (or_count = 0) and (n_count = 1) then fg[fg_sulfuric_acid_amide] := true;
        end;
      if (n_count = 2)   then fg[fg_sulfuric_acid_diamide] := true;
      if (hal_count > 0) then  fg[fg_sulfuryl_halide] := true;
    end;
  if (het_count = 1) and (c_count = 1) then   // sulfonic acid derivative
    begin
      fg[fg_sulfonic_acid_deriv] := true;
      if (o_count = 1) and (or_count = 0) then fg[fg_sulfonic_acid] := true;
      if (o_count = 1) and (or_count = 1) then fg[fg_sulfonic_acid_ester] := true;
      if (n_count = 1) then fg[fg_sulfonamide] := true;
      if (hal_count = 1) then fg[fg_sulfonyl_halide] := true;
    end;
  if (het_count = 0) and (c_count = 2) then   // sulfone
    begin
      fg[fg_sulfone] := true;
    end;
end;


procedure chk_p_deriv(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el, dbl_het : str2;
  het_count, oh_count, or_count, hal_count, n_count, c_count : integer;
begin
  if atom^[a_ref].element <>'P ' then exit;
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  dbl_het := '';
  het_count := 0; oh_count := 0; or_count := 0; hal_count := 0; n_count := 0; c_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'D' then
        begin
          dbl_het := atom^[(nb[i])].element;
        end;
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'S' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el = 'C ') then inc(c_count);
          if (is_hydroxy(a_ref,nb[i])) then inc(oh_count);
          if (is_alkoxy(a_ref,nb[i])) or (is_aryloxy(a_ref,nb[i])) then inc(or_count);
          if (nb_el = 'N ') then inc(n_count);
          if (nb_el = 'F ') or (nb_el = 'CL') or (nb_el = 'BR') or (nb_el = 'I ') then inc(hal_count);
        end;
    end;
  het_count := oh_count + or_count + hal_count + n_count;
  if (atom^[a_ref].atype = 'P3D') or (atom^[a_ref].atype = 'P4 ') then
    begin
      if (dbl_het = 'O ') then
        begin
          if (c_count = 0) then
            begin
              fg[fg_phosphoric_acid_deriv] := true;
              if (oh_count = 3)   then fg[fg_phosphoric_acid]        := true;
              if (or_count > 0)   then fg[fg_phosphoric_acid_ester]  := true;
              if (hal_count > 0)  then fg[fg_phosphoric_acid_halide] := true;            
              if (n_count > 0)    then fg[fg_phosphoric_acid_amide]  := true;
            end;
          if (c_count = 1) then
            begin
              fg[fg_phosphonic_acid_deriv] := true;
              if (oh_count = 2)   then fg[fg_phosphonic_acid]        := true;
              if (or_count > 0)   then fg[fg_phosphonic_acid_ester]  := true;
              //if (hal_count > 0)  then fg[fg_phosphonic_acid_halide] := true;            
              //if (n_count > 0)    then fg[fg_phosphonic_acid_amide]  := true;
            end;
          if (c_count = 3) then fg[fg_phosphinoxide] := true;  
        end;
      if (dbl_het = 'S ') then
        begin
          if (c_count = 0) then
            begin
              fg[fg_thiophosphoric_acid_deriv] := true;
              if (oh_count = 3)   then fg[fg_thiophosphoric_acid]        := true;
              if (or_count > 0)   then fg[fg_thiophosphoric_acid_ester]  := true;
              if (hal_count > 0)  then fg[fg_thiophosphoric_acid_halide] := true;            
              if (n_count > 0)    then fg[fg_thiophosphoric_acid_amide]  := true;
            end;
        end;
    end;
//  if (atom^[a_ref].atype = 'P4 ') then fg[fg_phosphoric_acid_deriv] := true;
  if (atom^[a_ref].atype = 'P3D') then
    begin
      if (c_count = 3) and (het_count = 0) then fg[fg_phosphine] := true;
      if (c_count = 3) and (oh_count = 1) then fg[fg_phosphinoxide] := true;
    end;
end;


procedure chk_b_deriv(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  het_count, oh_count, or_count, hal_count, n_count, c_count : integer;
begin
  if atom^[a_ref].element <>'B ' then exit;
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  het_count := 0; oh_count := 0; or_count := 0; hal_count := 0; n_count := 0; c_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'S' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el = 'C ') then inc(c_count) else if ((nb_el <>'H ') and
                                                      (nb_el <>'LP')) then inc(het_count);
          if (is_hydroxy(a_ref,nb[i])) then inc(oh_count);
          if (is_alkoxy(a_ref,nb[i])) or (is_aryloxy(a_ref,nb[i])) then inc(oh_count);
          if (nb_el = 'N ') then inc(n_count);
          if (nb_el = 'F ') or (nb_el = 'CL') or (nb_el = 'BR') or (nb_el = 'I ') then inc(hal_count);
        end;
    end;
//  het_count := oh_count + or_count + hal_count + n_count;
  if (c_count = 1) and (het_count = 2) then
    begin
      fg[fg_boronic_acid_deriv] := true;
      if (oh_count = 2)   then fg[fg_boronic_acid]        := true;
      if (or_count > 0)   then fg[fg_boronic_acid_ester]  := true;
    end;
end;


procedure chk_ammon(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  het_count, o_count, or_count, r_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  if (atom^[a_ref].atype <>'N3+') and (atom^[a_ref].formal_charge = 0) then exit;
  nb := get_neighbors(a_ref);
  het_count := 0; o_count := 0; or_count := 0; r_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'S' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el <> 'C ') and (nb_el <> 'H ') and
             (nb_el <> 'DU') then 
             begin
               inc(het_count);
               if (nb_el = 'O ') then begin
                 inc(o_count);
                 if atom^[(nb[i])].neighbor_count > 1 then inc(or_count);
               end;  
             end;
          if (is_alkyl(a_ref,nb[i])) or (is_aryl(a_ref,nb[i])) then inc(r_count);
        end; 
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'D' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el <> 'C ') then
            begin
              inc(het_count,2);
              if (nb_el = 'O ') then 
                begin
                  inc(o_count,2);
                end;  
            end;
          if nb_el = 'C ' then inc(r_count);
        end; 
      if (het_count = 0) and (r_count = 4) then fg[fg_quart_ammonium] := true;
      if (het_count = 1) and (atom^[a_ref].neighbor_count >= 3) then
        begin
          if (o_count = 1) and (or_count = 0) then fg[fg_n_oxide] := true;  // finds only aliphatic N-oxides!
          if ((((o_count = 1) and (or_count = 1)) or (o_count = 0))
           and (atom^[a_ref].arom = true)) then fg[fg_quart_ammonium] := true;
        end;
    end;
end;


procedure swap_atoms(var a1,a2:integer);
var
  a_tmp : integer;
begin
  a_tmp := a1;
  a1 := a2;
  a2 := a_tmp;
end;


procedure orient_bond(var a1,a2:integer);
var
  a1_el,a2_el : str2;
begin
  a1_el := atom^[a1].element;
  a2_el := atom^[a2].element;
  if (a1_el = 'H ') or (a2_el = 'H ') then exit;
  if (a2_el = 'C ') and (a1_el <> 'C ') then swap_atoms(a1,a2);
  if (a2_el = a1_el) then
    begin
      if hetbond_count(a1) > hetbond_count(a2) then swap_atoms(a1,a2);
    end;
  if (a2_el <> 'C ') and (a1_el <> 'C ') and (a1_el <> a2_el) then
    begin
      if (a1_el = 'O ') or (a2_el = 'O ') then
        begin
          if (a1_el = 'O ') then swap_atoms(a1,a2);
        end;
    end;
  if (a2_el <> 'C ') and (a1_el <> 'C ') and (a1_el = a2_el) then
    begin
      if ((atom^[a2].neighbor_count - hetbond_count(a2)) > 
          (atom^[a1].neighbor_count - hetbond_count(a1))) then swap_atoms(a1,a2);
    end;
end;


procedure chk_imine(a_ref,a_view:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  a_het : integer;
  a_c : integer;
  het_count, c_count : integer;
begin
  het_count := 0; c_count := 0;
  if (atom^[a_view].neighbor_count = 1) then
    begin
      if (atom^[a_ref].arom = false) then fg[fg_imine] := true;
    end else
    begin
      fillchar(nb,sizeof(neighbor_rec),0);
      nb := get_neighbors(a_view);
      if atom^[a_view].neighbor_count > 1 then
        begin
          for i := 1 to atom^[a_view].neighbor_count do
            begin
              if (nb[i] <> a_ref) and (bond^[(get_bond(a_view,nb[i]))].btype = 'S' ) then
                begin
                  nb_el := atom^[(nb[i])].element;
                  if (nb_el = 'C ') then 
                    begin
                      a_c := nb[i];
                      inc(c_count);
                    end;
                  if (nb_el = 'O ') or (nb_el = 'N ') then
                    begin
                      a_het := nb[i];
                      inc(het_count);
                    end;
                end; 
            end;
          if (c_count = 1) then 
            begin
              if ((is_alkyl(a_view,a_c)) or (is_aryl(a_view,a_c))) and
                 (atom^[a_ref].arom = false) then fg[fg_imine] := true;
            end;
          if (het_count) = 1 then 
            begin
              nb_el := atom^[a_het].element;
              if (nb_el = 'O ') then
                begin
                  if (is_hydroxy(a_view,a_het)) then fg[fg_oxime] := true;
                  if (is_alkoxy(a_view,a_het))  or (is_aryloxy(a_view,a_het)) then 
                    fg[fg_oxime_ether] := true;                  
                end;
              if (nb_el = 'N ') then
                begin
                  if (is_amino(a_view,a_het)) or (is_alkylamino(a_view,a_het)) or
                     (is_dialkylamino(a_view,a_het)) or (is_alkylarylamino(a_view,a_het)) or
                     (is_arylamino(a_view,a_het)) or (is_diarylamino(a_view,a_het)) then 
                    fg[fg_hydrazone] := true else
                    begin  // check for semicarbazone or thiosemicarbazone
                      fillchar(nb,sizeof(neighbor_rec),0);
                      nb := get_neighbors(a_het);

                      if atom^[a_het].neighbor_count > 1 then
                        begin
                          for i := 1 to atom^[a_het].neighbor_count do
                            begin
                              if (nb[i] <> a_view) then
                                begin
                                  if (is_carbamoyl(a_het,nb[i])) then fg[fg_semicarbazone] := true;
                                  if (is_thiocarbamoyl(a_het,nb[i])) then fg[fg_thiosemicarbazone] := true;                                
                                end;
                            end;
                        end; 
                    end;
                end;
            end;
        end;
    end;
end;


procedure chk_carbonyl_deriv(a_view,a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  c_count : integer;
  cn_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_view);
  c_count := 0; cn_count := 0;
  for i := 1 to atom^[a_view].neighbor_count do
    begin
      if bond^[(get_bond(a_view,nb[i]))].btype = 'S' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el = 'C ') then 
            begin
              if (is_cyano_c(nb[i])) then 
                begin
                  inc(cn_count) 
                end else 
                  begin 
                    inc(c_count);
                  end;
            end;
        end;
    end;     
  if is_oxo_c(a_view) then
    begin
      fg[fg_carbonyl]  := true;
      if (c_count + cn_count < 2) then fg[fg_aldehyde]  := true;
      if (c_count = 2) then 
        begin
          if (atom^[a_view].arom) then
            fg[fg_oxohetarene] := true else fg[fg_ketone]    := true;
        end;
      if (cn_count > 0) then fg[fg_acyl_cyanide] := true;
    end;
  if is_thioxo_c(a_view) then
    begin
      fg[fg_thiocarbonyl]  := true;
      if (c_count < 2) then fg[fg_thioaldehyde]  := true;
      if (c_count = 2) then
        begin
          if (atom^[a_view].arom) then
            fg[fg_thioxohetarene] := true else fg[fg_thioketone]    := true;
        end;
    end;
  if is_imino_c(a_view) then
    begin
      chk_imine(a_view,a_ref);
    end;
end;


procedure chk_carboxyl_deriv(a_view,a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  o_count, n_count, s_count : integer;
  a_o, a_n, a_s : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_view);
  o_count := 0; n_count := 0; s_count := 0;
  for i := 1 to atom^[a_view].neighbor_count do
    begin
      if bond^[(get_bond(a_view,nb[i]))].btype = 'S' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el <> 'C ') then 
            begin
              if nb_el = 'O ' then begin inc(o_count); a_o := nb[i]; end;
              if nb_el = 'N ' then begin inc(n_count); a_n := nb[i]; end;
              if nb_el = 'S ' then begin inc(s_count); a_s := nb[i]; end;
            end;
        end;
    end;     
  if (is_oxo_c(a_view)) then
    begin
      if (o_count = 1) then 
        begin  // anhydride is checked somewhere else
          if (bond^[get_bond(a_view,a_o)].arom = false) then fg[fg_carboxylic_acid_deriv]  := true;
          if (is_hydroxy(a_view,a_o)) then 
            begin
              if (atom^[a_o].formal_charge =  0) then fg[fg_carboxylic_acid] := true;
              if (atom^[a_o].formal_charge = -1) then fg[fg_carboxylic_acid_salt] := true;            
            end;
          if (is_alkoxy(a_view,a_o)) or (is_aryloxy(a_view,a_o)) then 
            begin
              if (bond^[get_bond(a_view,a_o)].arom = false) then fg[fg_carboxylic_acid_ester] := true;
              if (bond^[get_bond(a_view,a_o)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_o)].arom = true) then
                    //fg[fg_lactone_heteroarom] := true else fg[fg_lactone] := true;
                    fg[fg_oxohetarene] := true else fg[fg_lactone] := true;
                end;
            end;            
        end;
      if (n_count = 1) then 
        begin
          if (bond^[get_bond(a_view,a_n)].arom = false) then
            begin 
              fg[fg_carboxylic_acid_deriv]  := true;
            end else
            begin
              //fg[fg_lactam_heteroarom] := true;  // catches also pyridazines, 1,2,3-triazines, etc.
              fg[fg_oxohetarene] := true;
            end;
          if (is_amino(a_view,a_n)) or
             ((atom^[a_n].atype = 'NAM') and (atom^[a_n].neighbor_count = 1) ) then 
            begin
              fg[fg_carboxylic_acid_amide]      := true;
              fg[fg_carboxylic_acid_prim_amide] := true;
            end;
          if (is_alkylamino(a_view,a_n)) or (is_arylamino(a_view,a_n)) then 
            begin
              if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_amide]      := true;
              if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_sec_amide]  := true;
              if (bond^[get_bond(a_view,a_n)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_n)].arom = true) then
                    //fg[fg_lactam_heteroarom]    := true else 
                    fg[fg_oxohetarene]    := true else 
                    fg[fg_lactam]               := true;
                end;
            end;          
          if (is_dialkylamino(a_view,a_n)) or (is_alkylarylamino(a_view,a_n)) or
             (is_diarylamino(a_view,a_n)) then 
            begin
              if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_amide]      := true;
              if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_tert_amide] := true;
              if (bond^[get_bond(a_view,a_n)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_n)].arom = true) then
                    //fg[fg_lactam_heteroarom]    := true else 
                    fg[fg_oxohetarene]    := true else 
                    fg[fg_lactam]               := true;
                end;
            end;
          if (is_hydroxylamino(a_view,a_n)) then 
            begin
              fg[fg_hydroxamic_acid]            := true;
            end;
          if (is_hydrazino(a_view,a_n)) then 
            begin
              fg[fg_carboxylic_acid_hydrazide]  := true;
            end;
          if (is_azido(a_view,a_n)) then 
            begin
              fg[fg_carboxylic_acid_azide]  := true;
            end;
        end;
      if (s_count = 1) then 
        begin  // anhydride is checked somewhere else
          if (bond^[get_bond(a_view,a_s)].arom = false) then fg[fg_thiocarboxylic_acid_deriv]  := true;
          if (is_sulfanyl(a_view,a_s)) then 
            begin
              fg[fg_thiocarboxylic_acid] := true;
            end;
          if (is_alkylsulfanyl(a_view,a_s)) or (is_arylsulfanyl(a_view,a_s)) then 
            begin
              if (bond^[get_bond(a_view,a_s)].arom = false) then fg[fg_thiocarboxylic_acid_ester] := true;
              if (bond^[get_bond(a_view,a_s)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_s)].arom = true) then
                    //fg[fg_thiolactone_heteroarom] := true else fg[fg_thiolactone] := true;
                    fg[fg_oxohetarene] := true else fg[fg_thiolactone] := true;
                end;
            end;            
        end;
    end;  // end Oxo-C
  if (is_thioxo_c(a_view)) then
    begin
      // fg[fg_thiocarboxylic_acid_deriv]  := true;
      if (o_count = 1) then 
        begin  // anhydride is checked somewhere else
          if (bond^[get_bond(a_view,a_o)].arom = false) then fg[fg_thiocarboxylic_acid_deriv]  := true;
          if (is_hydroxy(a_view,a_o)) then 
            begin
              fg[fg_carboxylic_acid] := true;
            end;
          if (is_alkoxy(a_view,a_o)) or (is_aryloxy(a_view,a_o)) then 
            begin
              if (bond^[get_bond(a_view,a_s)].arom = false) then fg[fg_thiocarboxylic_acid_ester] := true;
              if (bond^[get_bond(a_view,a_o)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_o)].arom = true) then
                    //fg[fg_thiolactone_heteroarom] := true else fg[fg_thiolactone] := true;
                    fg[fg_thioxohetarene] := true else fg[fg_thiolactone] := true;
                end;
            end;            
        end;
      if (n_count = 1) then 
        begin
          // debug
          // mod_wrt_ln('checking for N attached to C=S');
          if (bond^[get_bond(a_view,a_n)].arom = false) then
            fg[fg_thiocarboxylic_acid_deriv]  := true else
            //fg[fg_thiolactam_heteroarom] := true;  // catches also pyridazines, 1,2,3-triazines, etc.
            fg[fg_thioxohetarene] := true;  // catches also pyridazines, 1,2,3-triazines, etc.
          if (is_amino(a_view,a_n)) then 
            begin
              // mod_wrt_ln('==> amino');
              fg[fg_thiocarboxylic_acid_amide]      := true;
              // fg[fg_thiocarboxylic_acid_prim_amide] := true;
            end;
          if (is_alkylamino(a_view,a_n)) or (is_arylamino(a_view,a_n)) then 
            begin
              // mod_wrt_ln('==> alkylamino or arylamino');
              if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_thiocarboxylic_acid_amide]      := true;
              //fg[fg_thiocarboxylic_acid_sec_amide]  := true;
              if (bond^[get_bond(a_view,a_n)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_n)].arom = true) then
                    //fg[fg_thiolactam_heteroarom] := true else fg[fg_thiolactam] := true;
                    fg[fg_thioxohetarene] := true else fg[fg_thiolactam] := true;
                end;
            end;          
          if (is_dialkylamino(a_view,a_n)) or (is_alkylarylamino(a_view,a_n)) or
             (is_diarylamino(a_view,a_n)) then 
            begin
              // mod_wrt_ln('==> dialkylamino or diarylamino');
              if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_thiocarboxylic_acid_amide]      := true;
              //fg[fg_thiocarboxylic_acid_tert_amide] := true;
              if (bond^[get_bond(a_view,a_n)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_n)].arom = true) then
                    //fg[fg_thiolactam_heteroarom] := true else fg[fg_thiolactam] := true;
                    fg[fg_thioxohetarene] := true else fg[fg_thiolactam] := true;
                end;
            end;          
        end;
      if (s_count = 1) then
        begin  // anhydride is checked somewhere else
          if (bond^[get_bond(a_view,a_s)].arom = false) then fg[fg_thiocarboxylic_acid_deriv]  := true;
          if (is_sulfanyl(a_view,a_s)) then 
            begin
              fg[fg_thiocarboxylic_acid] := true;
            end;
          if (is_alkylsulfanyl(a_view,a_s)) or (is_arylsulfanyl(a_view,a_s)) then 
            begin
              if (bond^[get_bond(a_view,a_s)].arom = false) then fg[fg_thiocarboxylic_acid_ester] := true;
              if (bond^[get_bond(a_view,a_s)].ring_count > 0) then
                begin
                  if (bond^[get_bond(a_view,a_s)].arom = true) then
                    //fg[fg_thiolactone_heteroarom] := true else fg[fg_thiolactone] := true;
                    fg[fg_thioxohetarene] := true else fg[fg_thiolactone] := true;
                end;
            end;            
        end;
    end;  // end Thioxo-C
  if (is_true_imino_c(a_view)) then
    begin
      // debug
      //mod_wrt_ln('checking for imino');
      //fg[fg_carboxylic_acid_deriv]  := true;
      if (o_count = 1) then 
        begin  
          if (bond^[get_bond(a_view,a_o)].arom = false) then fg[fg_carboxylic_acid_deriv]  := true;
          if (is_alkoxy(a_view,a_o)) or (is_aryloxy(a_view,a_o)) then 
            begin
              if (bond^[get_bond(a_view,a_o)].arom = false) then
                fg[fg_imido_ester] := true;
            end;            
        end;
      if (n_count = 1) and (bond^[get_bond(a_view,a_n)].arom = false) then 
        begin
          if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_deriv]  := true;
          if (is_amino(a_view,a_n)) or (is_subst_amino(a_view,a_n)) then
            begin
              if (bond^[get_bond(a_view,a_n)].arom = false) then 
                fg[fg_carboxylic_acid_deriv]  := true;fg[fg_carboxylic_acid_amidine]      := true;
            end;          
          if (is_hydrazino(a_view,a_n)) then 
            begin
              if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_amidrazone]  := true;
            end;
        end;
      if (n_count = 1) and (bond^[get_bond(a_view,a_n)].arom = true) then // catches also pyridazines, 1,2,3-triazines, etc.
        fg[fg_iminohetarene] := true;
      if (s_count = 1) then 
        begin  
          if (bond^[get_bond(a_view,a_s)].arom = false) then fg[fg_carboxylic_acid_deriv]  := true;
          if (is_alkylsulfanyl(a_view,a_s)) or (is_arylsulfanyl(a_view,a_s)) then 
            begin
              if (bond^[get_bond(a_view,a_s)].arom = false) then
                fg[fg_imido_thioester] := true;
            end;            
        end;
    end;
  if is_hydroximino_c(a_view) then
    begin
      if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_deriv]  := true;
      if (o_count = 1) then 
        begin  
          if (is_hydroxy(a_view,a_o)) then 
            begin
              fg[fg_hydroxamic_acid] := true;
            end;            
        end;
    end;
  if is_hydrazono_c(a_view) then
    begin
      //debug
      //mod_wrt_ln('checking for hydrazono');
      if (bond^[get_bond(a_view,a_n)].arom = false) then fg[fg_carboxylic_acid_deriv]  := true;
      if (n_count = 1) then 
        begin  
          if (is_amino(a_view,a_n)) or
             (is_subst_amino(a_view,a_n)) then 
            begin
              fg[fg_carboxylic_acid_amidrazone] := true;
            end;            
        end;
    end;
end;


procedure chk_co2_sp2(a_view,a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  o_count, or_count, n_count, nn_count, nnx_count, s_count, sr_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_view);
  o_count := 0; or_count := 0; n_count := 0;
  nn_count := 0; nnx_count := 0; s_count := 0; sr_count := 0;
  for i := 1 to atom^[a_view].neighbor_count do
    begin
      if bond^[(get_bond(a_view,nb[i]))].btype = 'S' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el <> 'C ') then
            begin
              if (nb_el = 'O ') then
                begin
                  inc(o_count);
                  if (is_alkoxy(a_view,nb[i])) or
                     (is_aryloxy(a_view,nb[i])) then inc (or_count);
                end;
              if (nb_el = 'N ') then 
                begin 
                  inc(n_count); 
                  if (is_hydrazino(a_view,nb[i])) then inc(nn_count);
                  if (is_subst_hydrazino(a_view,nb[i])) then inc(nnx_count);  // more general...
                end;
              if (nb_el = 'S ') then 
                begin 
                  inc(s_count); 
                  if (is_alkylsulfanyl(a_view,nb[i])) or 
                     (is_arylsulfanyl(a_view,nb[i])) then inc (sr_count);
                end;
            end;
        end;
    end;     
  if (is_oxo_c(a_view)) then
    begin
      if (o_count = 2) then 
        begin  
          fg[fg_carbonic_acid_deriv]  := true;
          if (or_count = 1) then fg[fg_carbonic_acid_monoester]  := true;
          if (or_count = 2) then fg[fg_carbonic_acid_diester]  := true;
        end;
      if (o_count = 1) and (s_count = 1) then 
        begin  
          fg[fg_thiocarbonic_acid_deriv]  := true;
          if (or_count + sr_count = 1) then fg[fg_thiocarbonic_acid_monoester]  := true;
          if (or_count + sr_count = 2) then fg[fg_thiocarbonic_acid_diester]  := true;
        end;
      if (s_count = 2) then 
        begin  
          fg[fg_thiocarbonic_acid_deriv]  := true;
          if (sr_count = 1) then fg[fg_thiocarbonic_acid_monoester]  := true;
          if (sr_count = 2) then fg[fg_thiocarbonic_acid_diester]  := true;
        end;
      if (o_count = 1) and (n_count = 1) then 
        begin  
          fg[fg_carbamic_acid_deriv]  := true;
          if (or_count = 0) then fg[fg_carbamic_acid]  := true;
          if (or_count = 1) then fg[fg_carbamic_acid_ester]  := true;
        end;
      if (s_count = 1) and (n_count = 1) then 
        begin  
          fg[fg_thiocarbamic_acid_deriv]  := true;
          if (sr_count = 0) then fg[fg_thiocarbamic_acid]  := true;
          if (sr_count = 1) then fg[fg_thiocarbamic_acid_ester]  := true;
        end;
      if (n_count = 2) then
        begin  
          if (nn_count = 1) then fg[fg_semicarbazide]  := true else
            begin
              if (nnx_count = 0) then fg[fg_urea] := true;  // excludes semicarbazones
            end;                     
        end;  
    end;  // end Oxo-C
  if (is_thioxo_c(a_view)) then
    begin
      if (o_count = 2) then 
        begin  
          fg[fg_thiocarbonic_acid_deriv]  := true;
          if (or_count = 1) then fg[fg_thiocarbonic_acid_monoester]  := true;
          if (or_count = 2) then fg[fg_thiocarbonic_acid_diester]  := true;
        end;
      if (o_count = 1) and (s_count = 1) then 
        begin  
          fg[fg_thiocarbonic_acid_deriv]  := true;
          if (or_count + sr_count = 1) then fg[fg_thiocarbonic_acid_monoester]  := true;
          if (or_count + sr_count = 2) then fg[fg_thiocarbonic_acid_diester]  := true;
        end;
      if (s_count = 2) then 
        begin  
          fg[fg_thiocarbonic_acid_deriv]  := true;
          if (sr_count = 1) then fg[fg_thiocarbonic_acid_monoester]  := true;
          if (sr_count = 2) then fg[fg_thiocarbonic_acid_diester]  := true;
        end;
      if (o_count = 1) and (n_count = 1) then 
        begin  
          fg[fg_thiocarbamic_acid_deriv]  := true;
          if (or_count = 0) then fg[fg_thiocarbamic_acid]  := true;
          if (or_count = 1) then fg[fg_thiocarbamic_acid_ester]  := true;
        end;
      if (s_count = 1) and (n_count = 1) then 
        begin  
          fg[fg_thiocarbamic_acid_deriv]  := true;
          if (sr_count = 0) then fg[fg_thiocarbamic_acid]  := true;
          if (sr_count = 1) then fg[fg_thiocarbamic_acid_ester]  := true;
        end;
      if (n_count = 2) then 
        begin  
          if (nn_count = 1) then fg[fg_thiosemicarbazide]  := true else
            begin
              if (nnx_count = 0) then fg[fg_thiourea] := true;  // excludes thiosemicarbazones
            end;                     
        end;  
    end;  // end Thioxo-C
  if (is_true_imino_c(a_view)) and (bond^[get_bond(a_view,a_ref)].arom = false) then
    begin
      if (o_count = 1) and (n_count = 1) then 
        begin  
          fg[fg_isourea]  := true;
        end;
      if (s_count = 1) and (n_count = 1) then 
        begin  
          fg[fg_isothiourea]  := true;
        end;
      if (n_count = 2) then 
        begin  
          fg[fg_guanidine] := true;
        end;  
    end;  // end Imino-C

end;


procedure chk_co2_sp(a_view,a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  o_count, n_count, s_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_view);
  o_count := 0; n_count := 0; s_count := 0;
  for i := 1 to atom^[a_view].neighbor_count do
    begin
      if bond^[(get_bond(a_view,nb[i]))].btype = 'D' then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el <> 'C ') then 
            begin
              if (nb_el = 'O ') then 
                begin 
                  inc(o_count); 
                end;
              if (nb_el = 'N ') then 
                begin 
                  inc(n_count); 
                end;
              if (nb_el = 'S ') then 
                begin 
                  inc(s_count); 
                end;
            end;
        end;
    end;     
  //if (o_count = 2) then fg[fg_carbon_dioxide] := true;
  //if (s_count = 2) then fg[fg_carbon_disulfide] := true;
  if (o_count = 1) and (n_count = 1) then fg[fg_isocyanate] := true;
  if (s_count = 1) and (n_count = 1) then fg[fg_isothiocyanate] := true;
  if (n_count = 2) then fg[fg_carbodiimide] := true;
end;


procedure chk_triple(a1,a2:integer);
var
  a1_el, a2_el : str2;
begin
  a1_el := atom^[a1].element;
  a2_el := atom^[a2].element;
  if (a1_el = 'C ') and (a2_el = 'C ') and (bond^[get_bond(a1,a2)].arom = false) then
    fg[fg_alkyne] := true;
  if (is_nitrile(a1,a2))     then fg[fg_nitrile]     := true;
  if (is_isonitrile(a1,a2))  then fg[fg_isonitrile]  := true;
  if (is_cyanate(a1,a2))     then fg[fg_cyanate]     := true;
  if (is_thiocyanate(a1,a2)) then fg[fg_thiocyanate] := true;
end;


procedure chk_ccx(a_view,a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  oh_count, or_count, n_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  oh_count := 0; or_count := 0; n_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'S' then
        begin
          if (is_hydroxy(a_ref,nb[i])) then inc(oh_count);
          if (is_alkoxy(a_ref,nb[i])) or 
             (is_aryloxy(a_ref,nb[i])) or
             (is_siloxy(a_ref,nb[i])) then inc(or_count);
          if (atom^[(nb[i])].atype = 'N3 ') or 
             (atom^[(nb[i])].atype = 'NAM') then inc(n_count);
        end;
    end;
  if (oh_count = 1) then fg[fg_enol]      := true;
  if (or_count = 1) then fg[fg_enolether] := true;  
  if (n_count = 1)  then fg[fg_enamine]   := true;
end;


procedure chk_xccx(a_view,a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  oh_count, or_count, n_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_view);
  oh_count := 0; or_count := 0; n_count := 0;
  for i := 1 to atom^[a_view].neighbor_count do
    begin
      if bond^[(get_bond(a_view,nb[i]))].btype = 'S' then
        begin
          if (is_hydroxy(a_view,nb[i])) then inc(oh_count);
          if (is_alkoxy(a_view,nb[i])) or
             (is_aryloxy(a_view,nb[i])) or
             (is_siloxy(a_view,nb[i])) then inc(or_count);
          if (atom^[(nb[i])].atype = 'N3 ') or
             (atom^[(nb[i])].atype = 'NAM') then inc(n_count);
        end;
    end;
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'S' then
        begin
          if (is_hydroxy(a_ref,nb[i])) then inc(oh_count);
          if (is_alkoxy(a_ref,nb[i])) or
             (is_aryloxy(a_ref,nb[i])) or
             (is_siloxy(a_ref,nb[i])) then inc(or_count);
          if (atom^[(nb[i])].atype = 'N3 ') or
             (atom^[(nb[i])].atype = 'NAM') then inc(n_count);
        end;
    end;
  if (oh_count = 2) then fg[fg_enediol]      := true;
end;


procedure chk_n_o_dbl(a1,a2:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  or_count, n_count, c_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a1);
  or_count := 0; n_count := 0; c_count := 0;
  for i := 1 to atom^[a1].neighbor_count do
    begin
      if (nb[i] <> a2) then
        begin
          nb_el := atom^[(nb[i])].element;
          if (nb_el = 'O ') then inc(or_count);
          if (nb_el = 'N ') then inc(n_count);
          if (nb_el = 'C ') then inc(c_count);
          // if (is_alkyl(a1,nb[i])) or (is_aryl(a1,nb[i])) then inc(c_count);
        end;
    end;
  if ((or_count + n_count + c_count) = 1) then  // excludes nitro etc.
    begin
      if (or_count = 1) then fg[fg_nitrite]       := true;
      if (c_count = 1)  then fg[fg_nitroso_compound] := true;
      // if (n_count = 1) then fg[fg_nitrosamine]   := true;  // still missing
    end;
  if ((c_count > 1) and (or_count = 0) and (n_count = 0)) then
    begin
      fg[fg_n_oxide] := true;
    end;
end;


procedure chk_sulfoxide(a1,a2:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  o_count, c_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a1);
  o_count := 0; c_count := 0;
  for i := 1 to atom^[a1].neighbor_count do
    begin
      nb_el := atom^[(nb[i])].element;
      if (nb_el = 'O ') then inc(o_count);
      if (is_alkyl(a1,nb[i])) or (is_aryl(a1,nb[i])) then inc(c_count);
    end;
  if (o_count = 1) and (c_count = 2) then fg[fg_sulfoxide] := true;
end;


procedure chk_double(a1,a2:integer);
var
  a1_el, a2_el : str2;
begin
  a1_el := atom^[a1].element;
  a2_el := atom^[a2].element;
  if (a1_el = 'C ') and (a2_el <> 'C ') and (bond^[get_bond(a1,a2)].arom = false) then
    begin
      if (hetbond_count(a1) = 2) then
        begin
          chk_carbonyl_deriv(a1,a2);
        end;
      if (hetbond_count(a1) = 3) then
        begin
          chk_carboxyl_deriv(a1,a2);
        end;
      if (hetbond_count(a1) = 4) then
        begin
          if (atom^[a1].atype = 'C2 ') then chk_co2_sp2(a1,a2);
          if (atom^[a1].atype = 'C1 ') then chk_co2_sp(a1,a2);
        end;
    end;  // end C=X
  if (atom^[a1].atype = 'C2 ') and (atom^[a2].atype = 'C2 ') and 
     (bond^[get_bond(a1,a2)].arom = false) then
    begin
      if (hetbond_count(a1) = 0) and (hetbond_count(a2) = 2) then fg[fg_ketene_acetal_deriv] := true;
      if (hetbond_count(a1) = 0) and (hetbond_count(a2) = 1) then chk_ccx(a1,a2);
      if (hetbond_count(a1) = 1) and (hetbond_count(a2) = 1) then chk_xccx(a1,a2);
      if (hetbond_count(a1) = 0) and (hetbond_count(a2) = 0) and
         (atom^[a1].arom = false) and (atom^[a2].arom = false) then fg[fg_alkene] := true;
    end;
  if (a1_el = 'N ') and (a2_el = 'N ') and 
     (hetbond_count(a1) = 2) and (hetbond_count(a2) = 2) and
     (bond^[get_bond(a1,a2)].arom = false) and
     (atom^[a1].neighbor_count = 2) and (atom^[a2].neighbor_count = 2) 
       then fg[fg_azo_compound] := true;
  if (a1_el = 'N ') and (a2_el = 'O ') then chk_n_o_dbl(a1,a2);
  if (a1_el = 'S ') and (a2_el = 'O ') then chk_sulfoxide(a1,a2);
end;


procedure chk_c_hal(a1,a2:integer);
var
  a2_el : str2;
begin
  a2_el := atom^[a2].element;
  fg[fg_halogen_deriv] := true;
  if atom^[a1].arom then begin
    fg[fg_aryl_halide] := true;
    if (a2_el = 'F ') then fg[fg_aryl_fluoride] := true;
    if (a2_el = 'CL') then fg[fg_aryl_chloride] := true;
    if (a2_el = 'BR') then fg[fg_aryl_bromide]  := true;
    if (a2_el = 'I ') then fg[fg_aryl_iodide]   := true;
  end else
  begin
    if (atom^[a1].atype = 'C3 ') and (hetbond_count(a1) <= 2) then
      begin  // alkyl halides
        fg[fg_alkyl_halide] := true;
        if (a2_el = 'F ') then fg[fg_alkyl_fluoride] := true;
        if (a2_el = 'CL') then fg[fg_alkyl_chloride] := true;
        if (a2_el = 'BR') then fg[fg_alkyl_bromide]  := true;
        if (a2_el = 'I ') then fg[fg_alkyl_iodide]   := true;                      
      end;
    if (atom^[a1].atype = 'C2 ') and (hetbond_count(a1) = 3) then
      begin  // acyl halides and related compounds
        if (is_oxo_c(a1)) then
          begin
            fg[fg_acyl_halide] := true;
            if (a2_el = 'F ') then fg[fg_acyl_fluoride] := true;
            if (a2_el = 'CL') then fg[fg_acyl_chloride] := true;
            if (a2_el = 'BR') then fg[fg_acyl_bromide]  := true;
            if (a2_el = 'I ') then fg[fg_acyl_iodide]   := true;                      
          end;
        if (is_thioxo_c(a1)) then
          begin
            fg[fg_thiocarboxylic_acid_deriv] := true;
          end;
        if (is_imino_c(a1)) then
          begin
            fg[fg_imidoyl_halide] := true;
          end;
      end;
    if (atom^[a1].atype = 'C2 ') and (hetbond_count(a1) = 4) then
      begin  // chloroformates etc.
        fg[fg_co2_deriv] := true;
        if (is_oxo_c(a1)) then
          begin
            fg[fg_carbonic_acid_deriv] := true;
            if (is_alkoxycarbonyl(a2,a1)) or (is_aryloxycarbonyl(a2,a1)) then 
              fg[fg_carbonic_acid_ester_halide] := true;
            if (is_carbamoyl(a2,a1)) then 
              begin 
                fg[fg_carbamic_acid_deriv]  := true;
                fg[fg_carbamic_acid_halide] := true;
              end;
          end;
        if (is_thioxo_c(a1)) then
          begin
            fg[fg_thiocarbonic_acid_deriv] := true;
            if (is_alkoxythiocarbonyl(a2,a1)) or (is_aryloxythiocarbonyl(a2,a1)) then 
              fg[fg_thiocarbonic_acid_ester_halide] := true;
            if (is_thiocarbamoyl(a2,a1)) then 
              begin 
                fg[fg_thiocarbamic_acid_deriv]  := true;
                fg[fg_thiocarbamic_acid_halide] := true;
              end;
          end;
      end;
    // still missing: polyhalogen compounds (-CX2H, -CX3)
  end;    // end of non-aromatic halogen compounds
end;


procedure chk_c_o(a1,a2:integer);
begin
  // ignore heteroaromatic rings (like furan, thiophene, etc.)
  if (bond^[get_bond(a1,a2)].arom = true) then exit;
  if (is_true_alkyl(a2,a1)) and (is_hydroxy(a1,a2)) then
    begin
      fg[fg_hydroxy] := true;
      fg[fg_alcohol] := true;
      if atom^[a1].neighbor_count <= 2 then fg[fg_prim_alcohol] := true;
      if atom^[a1].neighbor_count = 3 then fg[fg_sec_alcohol]  := true;
      if atom^[a1].neighbor_count = 4 then fg[fg_tert_alcohol] := true;
    end;
  if (is_aryl(a2,a1)) and (is_hydroxy(a1,a2)) then
    begin
      fg[fg_hydroxy] := true;
      fg[fg_phenol]  := true;
      // still missing: check for 1,2-diphenol
    end;
  if (is_true_alkyl(a2,a1)) and (is_true_alkoxy(a1,a2)) then
    begin
      fg[fg_ether]        := true;
      fg[fg_dialkylether] := true;
    end;
  if ((is_true_alkyl(a2,a1)) and (is_aryloxy(a1,a2))) or 
     ((is_aryl(a2,a1)) and (is_true_alkoxy(a1,a2))) then
    begin
      fg[fg_ether]        := true;
      fg[fg_alkylarylether] := true;
    end;
  if (is_aryl(a2,a1)) and (is_aryloxy(a1,a2)) then
    begin
      fg[fg_ether]        := true;
      fg[fg_diarylether] := true;
    end;
end;


procedure chk_c_s(a1,a2:integer);
var
  i : integer;
  nb : neighbor_rec;
  nb_el : str2;
  o_count, oh_count, or_count, n_count, c_count, hal_count : integer;
begin
  // ignore heteroaromatic rings (like furan, thiophene, etc.)
  if (bond^[get_bond(a1,a2)].arom = true) then exit;
  if (is_alkyl(a2,a1)) and (is_sulfanyl(a1,a2)) then
    begin
      fg[fg_thiol] := true;
      fg[fg_alkylthiol] := true;
    end;
  if (is_aryl(a2,a1)) and (is_sulfanyl(a1,a2)) then
    begin
      fg[fg_thiol]       := true;
      fg[fg_arylthiol]   := true;
    end;
  if (is_true_alkyl(a2,a1)) and (is_true_alkylsulfanyl(a1,a2)) then fg[fg_thioether]  := true;
  if ((is_true_alkyl(a2,a1)) and (is_arylsulfanyl(a1,a2))) or 
     ((is_aryl(a2,a1)) and (is_true_alkylsulfanyl(a1,a2))) then fg[fg_thioether] := true;
  if (is_aryl(a2,a1)) and (is_arylsulfanyl(a1,a2)) then fg[fg_thioether]    := true;
  // check for sulfinic/sulfenic acid derivatives
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a2);
  o_count := 0; oh_count := 0; or_count := 0; n_count := 0; c_count := 0; hal_count := 0;
  for i := 1 to atom^[a2].neighbor_count do
    begin
      nb_el := atom^[(nb[i])].element;
      if (is_alkyl(a2,nb[i])) or (is_aryl(a2,nb[i])) then inc(c_count);
      if (is_hydroxy(a2,nb[i])) then inc(oh_count);        
      if (is_alkoxy(a2,nb[i])) or (is_aryloxy(a2,nb[i])) then inc(or_count);
      if (is_amino(a2,nb[i])) or (is_subst_amino(a2,nb[i])) then inc(n_count);
      if (nb_el = 'F ') or (nb_el = 'CL') or (nb_el = 'BR') or (nb_el = 'I ') then inc(hal_count);
      if (nb_el = 'O ') then inc(o_count);
    end;  
  if (c_count = 1) then
    begin
      if (atom^[a2].neighbor_count = 3) and
         ((o_count - (oh_count + or_count)) = 1) then    // sulfinic acid & derivs
        begin
          fg[fg_sulfinic_acid_deriv]  := true;
          if (oh_count = 1)  then fg[fg_sulfinic_acid]        := true;
          if (or_count = 1)  then fg[fg_sulfinic_acid_ester]  := true;
          if (hal_count = 1) then fg[fg_sulfinic_acid_halide] := true;
          if (n_count = 1)   then fg[fg_sulfinic_acid_amide]  := true;
        end;
      if (atom^[a2].neighbor_count = 2) and
         ((o_count - (oh_count + or_count)) = 0) then    // sulfenic acid & derivs
        begin
          fg[fg_sulfenic_acid_deriv]  := true;
          if (oh_count = 1)  then fg[fg_sulfenic_acid]        := true;
          if (or_count = 1)  then fg[fg_sulfenic_acid_ester]  := true;
          if (hal_count = 1) then fg[fg_sulfenic_acid_halide] := true;
          if (n_count = 1)   then fg[fg_sulfenic_acid_amide]  := true;
        end;
    
    end;
end;


procedure chk_c_n(a1,a2:integer);
begin
  // ignore heteroaromatic rings (like furan, thiophene, pyrrol, etc.)
  if (atom^[a2].arom = true) then exit;
  if (is_true_alkyl(a2,a1)) and (is_amino(a1,a2)) then
    begin
      fg[fg_amine]            := true;
      fg[fg_prim_amine]       := true;
      fg[fg_prim_aliph_amine] := true;
    end;
  if (is_aryl(a2,a1)) and (is_amino(a1,a2)) then
    begin
      fg[fg_amine]            := true;
      fg[fg_prim_amine]       := true;
      fg[fg_prim_arom_amine]  := true;
    end;
  if (is_true_alkyl(a2,a1)) and (is_true_alkylamino(a1,a2)) then
    begin
      fg[fg_amine]            := true;
      fg[fg_sec_amine]        := true;
      fg[fg_sec_aliph_amine]  := true;
    end;
  if (is_aryl(a2,a1)) and (is_true_alkylamino(a1,a2)) then
    begin
      fg[fg_amine]            := true;
      fg[fg_sec_amine]        := true;
      fg[fg_sec_mixed_amine]  := true;
    end;
  if (is_aryl(a2,a1)) and (is_arylamino(a1,a2)) then
    begin
      fg[fg_amine]            := true;
      fg[fg_sec_amine]        := true;
      fg[fg_sec_arom_amine]   := true;
    end;
  if (is_true_alkyl(a2,a1)) and (is_true_dialkylamino(a1,a2)) then
    begin
      fg[fg_amine]            := true;
      fg[fg_tert_amine]       := true;
      fg[fg_tert_aliph_amine] := true;
    end;
  if ((is_true_alkyl(a2,a1)) and (is_diarylamino(a1,a2))) or
     ((is_aryl(a2,a1)) and (is_true_dialkylamino(a1,a2))) then
    begin
      fg[fg_amine]            := true;
      fg[fg_tert_amine]       := true;
      fg[fg_tert_mixed_amine] := true;
    end;
  if (is_aryl(a2,a1)) and (is_diarylamino(a1,a2)) then
    begin
      fg[fg_amine]            := true;
      fg[fg_tert_amine]       := true;
      fg[fg_tert_arom_amine]  := true;
    end;
  if ((is_alkyl(a2,a1)) or (is_aryl(a2,a1))) and (is_hydroxylamino(a1,a2)) then
    fg[fg_hydroxylamine]      := true;
  if ((is_alkyl(a2,a1)) or (is_aryl(a2,a1)) or (is_acyl(a2,a1))) and (is_hydrazino(a1,a2)) then
    fg[fg_hydrazine]           := true;
  if ((is_alkyl(a2,a1)) or (is_aryl(a2,a1))) and (is_azido(a1,a2)) then
    fg[fg_azide]           := true;
  if ((is_alkyl(a2,a1)) or (is_aryl(a2,a1))) and (is_diazonium(a1,a2)) then
    fg[fg_diazonium_salt]           := true;
  if ((is_alkyl(a2,a1)) or (is_aryl(a2,a1))) and (is_nitro(a1,a2)) then
    fg[fg_nitro_compound]     := true;
end;


procedure chk_c_c(a1,a2:integer);
var
  i : integer;
  nb : neighbor_rec;
  oh_count, nhr_count : integer;
begin
  // ignore aromatic rings
  if (atom^[a2].arom = true) then exit;
  //check for 1,2-diols and 1,2-aminoalcoholes
  if (atom^[a1].atype = 'C3 ') and (atom^[a2].atype = 'C3 ') then
    begin
      if (hetbond_count(a1) = 1) and (hetbond_count(a2) = 1) then
        begin
          oh_count := 0; nhr_count := 0;
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(a1);
          for i := 1 to atom^[a1].neighbor_count do
            begin
              if nb[i] <> a2 then
                begin
                  if (is_hydroxy(a1,nb[i])) then inc(oh_count);        
                  if (is_amino(a1,nb[i])) or (is_alkylamino(a1,nb[i])) 
                      or (is_arylamino(a1,nb[i])) then inc(nhr_count);
                end;
            end;  
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(a2);
          for i := 1 to atom^[a2].neighbor_count do
            begin
              if nb[i] <> a1 then
                begin
                  if (is_hydroxy(a2,nb[i])) then inc(oh_count);        
                  if (is_amino(a2,nb[i])) or (is_alkylamino(a2,nb[i])) 
                      or (is_arylamino(a2,nb[i])) then inc(nhr_count);
                end;
            end;
          if oh_count = 2 then fg[fg_1_2_diol] := true;
          if (oh_count = 1) and (nhr_count = 1) then fg[fg_1_2_aminoalcohol] := true;
        end;
    end;
  // check for alpha-aminoacids and alpha-hydroxyacids
  if (atom^[a1].atype = 'C3 ') and (atom^[a2].atype = 'C2 ') then
    begin
      if (hetbond_count(a1) = 1) and (hetbond_count(a2) = 3) then
        begin
          oh_count := 0; nhr_count := 0;
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(a1);
          for i := 1 to atom^[a1].neighbor_count do
            begin
              if nb[i] <> a2 then
                begin
                  if (is_hydroxy(a1,nb[i])) then inc(oh_count);        
                  if (is_amino(a1,nb[i])) or (is_alkylamino(a1,nb[i])) 
                      or (is_arylamino(a1,nb[i])) then inc(nhr_count);
                end;
            end;  
          fillchar(nb,sizeof(neighbor_rec),0);
          nb := get_neighbors(a2);
          for i := 1 to atom^[a2].neighbor_count do
            begin
              if nb[i] <> a1 then
                begin
                  if (is_hydroxy(a2,nb[i])) then inc(oh_count);        
                end;
            end;  
          if (oh_count = 2) and (is_oxo_C(a2)) then fg[fg_alpha_hydroxyacid] := true;
          if (oh_count = 1) and (nhr_count = 1) and (is_oxo_C(a2)) then 
            fg[fg_alpha_aminoacid] := true;
        end;
    end;    
end;


procedure chk_x_y_single(a_view,a_ref:integer);
begin
  if (atom^[a_view].atype = 'O3 ') and (atom^[a_ref].atype = 'O3 ') then
    begin
      if (is_hydroxy(a_ref,a_view)) or
         (is_hydroxy(a_view,a_ref)) then fg[fg_hydroperoxide] := true;
      if ((is_alkoxy(a_ref,a_view)) or
          (is_aryloxy(a_ref,a_view)) or 
          (is_siloxy(a_ref,a_view))) and
         ((is_alkoxy(a_view,a_ref)) or 
          (is_aryloxy(a_view,a_ref)) or
          (is_siloxy(a_view,a_ref))) then fg[fg_peroxide] := true;
    end;  // still missing: peracid
  if (atom^[a_view].atype = 'S3 ') and (atom^[a_ref].atype = 'S3 ') then
    begin
      if (atom^[a_view].neighbor_count = 2) and (atom^[a_ref].neighbor_count = 2) then
        fg[fg_disulfide] := true;
    end;
  if (atom^[a_view].element = 'N ') and (atom^[a_ref].element = 'N ') and
     (hetbond_count(a_view) = 1) and (hetbond_count(a_ref) = 1) then
    begin
      //if ((is_amino(a_ref,a_view)) or 
      //    (is_subst_amino(a_ref,a_view)) or
      //    (is_acylamino(a_ref,a_view))) and
      //   ((is_amino(a_view,a_ref)) or 
      //    (is_subst_amino(a_view,a_ref)) or
      //    (is_acylamino(a_ref,a_view))) then 
      if (bond^[get_bond(a_view,a_ref)].arom = false) then fg[fg_hydrazine] := true;
    end; 
  if (atom^[a_view].element = 'N ') and (atom^[a_ref].atype = 'O3 ') then
    begin  // bond is in "opposite" direction
      if ((is_alkoxy(a_view,a_ref)) or (is_aryloxy(a_view,a_ref))) and
         (is_nitro(a_ref,a_view)) then fg[fg_nitrate] := true;
    end;
  if (atom^[a_view].element = 'S ') and (atom^[a_ref].element = 'O ') then    
    chk_sulfoxide(a_view,a_ref);
end;


procedure chk_single(a1,a2:integer);
var
  a1_el, a2_el : str2;
begin
  a1_el := atom^[a1].element;
  a2_el := atom^[a2].element;
  if (a1_el = 'C ') and 
     ((a2_el = 'F ') or (a2_el = 'CL') or (a2_el = 'BR') or (a2_el = 'I ')) then chk_c_hal(a1,a2);
  if (a1_el = 'C ') and (a2_el = 'O ') then chk_c_o(a1,a2);   
  if (a1_el = 'C ') and (a2_el = 'S ') then chk_c_s(a1,a2);   
  if (a1_el = 'C ') and (a2_el = 'N ') then chk_c_n(a1,a2);       
  if (a1_el = 'C ') and (is_metal(a2) and (is_cyano_c(a1) = false)) then
    begin
      fg[fg_organometallic] := true;
      if (a2_el = 'LI') then fg[fg_organolithium] := true;
      if (a2_el = 'MG') then fg[fg_organomagnesium] := true;
    end; 
  if (a1_el = 'C ') and (a2_el = 'C ') then chk_c_c(a1,a2);       
  if (a1_el <> 'C ') and (a2_el <> 'C ') then chk_x_y_single(a1,a2);
end;


procedure chk_carbonyl_deriv_sp3(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  oh_count, or_count, n_count, sh_count, sr_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  oh_count := 0; or_count := 0; n_count := 0; sh_count := 0; sr_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if (is_hydroxy(a_ref,nb[i])) then inc(oh_count);
      if (is_alkoxy(a_ref,nb[i])) or (is_aryloxy(a_ref,nb[i])) then inc(or_count);
      if (is_sulfanyl(a_ref,nb[i])) then inc(sh_count);
      if (is_alkylsulfanyl(a_ref,nb[i])) or (is_arylsulfanyl(a_ref,nb[i])) then inc(sr_count);
      if (atom^[(nb[i])].atype = 'N3 ') or (atom^[(nb[i])].atype = 'NAM') then inc(n_count);
    end;
  if (oh_count = 2) then fg[fg_carbonyl_hydrate] := true;
  if (oh_count = 1) and (or_count = 1) then fg[fg_hemiacetal] := true;
  if (or_count = 2) then fg[fg_acetal] := true;
  if ((oh_count = 1) or (or_count = 1)) and (n_count = 1) then fg[fg_hemiaminal] := true;  
  if (n_count = 2) then fg[fg_aminal] := true;  
  if ((sh_count = 1) or (sr_count = 1)) and (n_count = 1) then fg[fg_thiohemiaminal] := true;  
  if (sr_count = 2) then fg[fg_thioacetal] := true;  
end;


procedure chk_carboxyl_deriv_sp3(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  or_count, n_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  or_count := 0; n_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if (is_alkoxy(a_ref,nb[i])) or
         (is_aryloxy(a_ref,nb[i])) or
         (is_siloxy(a_ref,nb[i])) then inc(or_count);
      if (atom^[(nb[i])].atype = 'N3 ') or (atom^[(nb[i])].atype = 'NAM') then inc(n_count);
    end;
  if (or_count + n_count > 1) then fg[fg_orthocarboxylic_acid_deriv] := true;
  if (or_count = 3) then fg[fg_carboxylic_acid_orthoester] := true;
  if (or_count = 2) and (n_count = 1) then fg[fg_carboxylic_acid_amide_acetal] := true;
end;


procedure chk_anhydride(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  acyl_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  acyl_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if (is_acyl(a_ref,nb[i])) or (is_carbamoyl(a_ref,nb[i])) then inc(acyl_count); 
    end;
  if (acyl_count = 2) and (atom^[a_ref].atype = 'O3 ') then
    begin
      fg[fg_carboxylic_acid_deriv]     := true;
      fg[fg_carboxylic_acid_anhydride] := true;
    end;
end;


procedure chk_imide(a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  acyl_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  acyl_count := 0;
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if (is_acyl(a_ref,nb[i])) or (is_carbamoyl(a_ref,nb[i])) then inc(acyl_count);
    end;
  if (acyl_count = 2) and (atom^[a_ref].element = 'N ') then
    begin
      fg[fg_carboxylic_acid_deriv]     := true;
      fg[fg_carboxylic_acid_imide] := true;
      if (atom^[a_ref].neighbor_count = 2) then 
        fg[fg_carboxylic_acid_unsubst_imide] := true;
      if (atom^[a_ref].neighbor_count = 3) then 
        fg[fg_carboxylic_acid_subst_imide] := true;
    end;
end;


procedure chk_12diphenol(a_view,a_ref:integer);
var
  i : integer;
  nb : neighbor_rec;
  oh_count : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_view);
  oh_count := 0;
  for i := 1 to atom^[a_view].neighbor_count do
    begin
      if bond^[(get_bond(a_view,nb[i]))].btype = 'S' then
        begin
          if (is_hydroxy(a_view,nb[i])) then inc(oh_count);
        end;
    end;
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  for i := 1 to atom^[a_ref].neighbor_count do
    begin
      if bond^[(get_bond(a_ref,nb[i]))].btype = 'S' then
        begin
          if (is_hydroxy(a_ref,nb[i])) then inc(oh_count);
        end;
    end;
  if (oh_count = 2) then fg[fg_1_2_diphenol] := true;
end;


procedure chk_arom_fg(a1,a2:integer);
begin
  if (hetbond_count(a1) = 1) and (hetbond_count(a2) = 1) then chk_12diphenol(a1,a2);
end;

function is_arene(r_id:integer):boolean;
var
  i,j  : integer;
  r    : boolean;
  testring : ringpath_type;
  ring_size : integer;
  a_prev, a_ref : integer;
begin
  r := false;
  if (r_id < 1) or (r_id > n_rings) then begin is_arene := false; exit; end;
  fillchar(testring,sizeof(ringpath_type),0);
  for j := 1 to max_ringsize do if ring^[r_id,j] > 0 then testring[j] := ring^[r_id,j];
  ring_size := path_length(testring);
  if (ring_size > 2) then
    begin
      r := true;
      a_prev := testring[ring_size];
      for i := 1 to ring_size do
        begin
          a_ref := testring[i];
          if (bond^[get_bond(a_prev,a_ref)].arom = false) then r := false;
          a_prev := a_ref;
        end;
    end;
  is_arene := r;
end;


function is_heterocycle(r_id:integer):boolean;
var
  i,j  : integer;
  r    : boolean;
  testring : ringpath_type;
  ring_size : integer;
  a_ref : integer;
begin
  r := false;
  if (r_id < 1) or (r_id > n_rings) then begin is_heterocycle := false; exit; end;
  fillchar(testring,sizeof(ringpath_type),0);
  for j := 1 to max_ringsize do if ring^[r_id,j] > 0 then testring[j] := ring^[r_id,j];
  ring_size := path_length(testring);
  if (ring_size > 2) then
    begin
      for i := 1 to ring_size do
        begin
          a_ref := testring[i];
          if (atom^[a_ref].element <> 'C ') then r := true;
        end;
    end;
  is_heterocycle := r;
end;


procedure chk_oxo_thioxo_imino_hetarene(r_id:integer);
var
  i,j  : integer;
  testring : ringpath_type;
  ring_size : integer;
  a_ref : integer;
begin
  if (r_id < 1) or (r_id > n_rings) then exit;
  fillchar(testring,sizeof(ringpath_type),0);
  for j := 1 to max_ringsize do if ring^[r_id,j] > 0 then testring[j] := ring^[r_id,j];
  ring_size := path_length(testring);
  if (is_arene(r_id)) and (odd(ring_size) = false) then
    begin
      for i := 1 to ring_size do
        begin
          a_ref := testring[i];
          if (is_oxo_c(a_ref)) then fg[fg_oxohetarene]  := true;
          if (is_thioxo_c(a_ref)) then fg[fg_thioxohetarene]  := true;
          if (is_exocyclic_imino_c(a_ref,r_id)) then fg[fg_iminohetarene] := true;
        end;
    end;
end;


procedure chk_ion(a_ref:integer);
var
  i      : integer;
  nb     : neighbor_rec;
  charge : integer;
begin
  fillchar(nb,sizeof(neighbor_rec),0);
  nb := get_neighbors(a_ref);
  charge := atom^[a_ref].formal_charge;
  if charge <> 0 then
    begin  // check if charge is neutralized by an adjacent opposite charge
      for i := 1 to atom^[a_ref].neighbor_count do
        begin
          charge := charge + atom^[(nb[i])].formal_charge;
        end;
      if charge > 0 then fg[fg_cation] := true;
      if charge < 0 then fg[fg_anion]  := true;
    end;
end;


procedure chk_functionalgroups;
var
  i : integer;
  a1,a2 : integer;
  bt : char;
  pos_chg, neg_chg : integer;
begin
  if (n_atoms < 1) or (n_bonds < 1) then exit;
  pos_chg := 0; neg_chg := 0;
  for i := 1 to n_atoms do  // a few groups are best discovered in the atom list
    begin
      if (atom^[i].atype = 'SO2') then chk_so2_deriv(i);
      //if (atom^[i].atype = 'SO ') then fg[fg_sulfoxide] := true;  // do another check in the bond list!!
      if (atom^[i].element = 'P ') then chk_p_deriv(i);
      if (atom^[i].element = 'B ') then chk_b_deriv(i);
      if (atom^[i].atype = 'N3+') or (atom^[i].formal_charge > 0) then chk_ammon(i);
      if (atom^[i].atype = 'C3 ') and (hetbond_count(i) = 2) then chk_carbonyl_deriv_sp3(i);
      if (atom^[i].atype = 'C3 ') and (hetbond_count(i) = 3) then chk_carboxyl_deriv_sp3(i);
      if (atom^[i].atype = 'O3 ') and (atom^[i].neighbor_count = 2) then chk_anhydride(i);
      if ((atom^[i].atype = 'N3 ') or (atom^[i].atype = 'NAM')) and
          (atom^[i].neighbor_count >= 2) then chk_imide(i);
      if (atom^[i].formal_charge > 0) then pos_chg := pos_chg + atom^[i].formal_charge;
      if (atom^[i].formal_charge < 0) then neg_chg := neg_chg + atom^[i].formal_charge;
      chk_ion(i);
    end;
  for i := 1 to n_bonds do  // most groups are best discovered in the bond list
    begin
      a1 := bond^[i].a1;
      a2 := bond^[i].a2;
      bt := bond^[i].btype;
      if (is_heavyatom(a1)) and (is_heavyatom(a2)) then
        begin
          orient_bond(a1,a2);
          if (bt = 'T') then chk_triple(a1,a2);
          if (bt = 'D') then chk_double(a1,a2);
          if (bt = 'S') then chk_single(a1,a2);
          if (bond^[i].arom) then chk_arom_fg(a1,a2);
        end;
    end;
  if (n_rings > 0) then
    begin
      for i := 1 to n_rings do
        begin
          chk_oxo_thioxo_imino_hetarene(i);
          if (is_arene(i)) then fg[fg_aromatic] := true;
          if (is_heterocycle(i)) then fg[fg_heterocycle] := true;
        end;
    end;
  if (pos_chg + neg_chg) > 0 then fg[fg_cation] := true;
  if (pos_chg + neg_chg) < 0 then fg[fg_anion]  := true;
end;


procedure mod_wrt_noln_fg_text;
begin
  if fg[fg_cation]                         then mod_wrt_ln('cation');
  if fg[fg_anion]                          then mod_wrt_ln('anion');
//  if fg[fg_carbonyl]                       then mod_wrt_ln('carbonyl compound');
  if fg[fg_aldehyde]                       then mod_wrt_ln('aldehyde');
  if fg[fg_ketone]                         then mod_wrt_ln('ketone');
//  if fg[fg_thiocarbonyl]                   then mod_wrt_ln('thiocarbonyl compound');
  if fg[fg_thioaldehyde]                   then mod_wrt_ln('thioaldehyde');
  if fg[fg_thioketone]                     then mod_wrt_ln('thioketone');
  if fg[fg_imine]                          then mod_wrt_ln('imine');
  if fg[fg_hydrazone]                      then mod_wrt_ln('hydrazone');
  if fg[fg_semicarbazone]                  then mod_wrt_ln('semicarbazone');
  if fg[fg_thiosemicarbazone]              then mod_wrt_ln('thiosemicarbazone');
  if fg[fg_oxime]                          then mod_wrt_ln('oxime');
  if fg[fg_oxime_ether]                    then mod_wrt_ln('oxime ether');
  if fg[fg_ketene]                         then mod_wrt_ln('ketene');
  if fg[fg_ketene_acetal_deriv]            then mod_wrt_ln('ketene acetal or derivative');
  if fg[fg_carbonyl_hydrate]               then mod_wrt_ln('carbonyl hydrate');
  if fg[fg_hemiacetal]                     then mod_wrt_ln('hemiacetal');
  if fg[fg_acetal]                         then mod_wrt_ln('acetal');
  if fg[fg_hemiaminal]                     then mod_wrt_ln('hemiaminal');
  if fg[fg_aminal]                         then mod_wrt_ln('aminal');
  if fg[fg_thiohemiaminal]                 then mod_wrt_ln('hemithioaminal');
  if fg[fg_thioacetal]                     then mod_wrt_ln('thioacetal');
  if fg[fg_enamine]                        then mod_wrt_ln('enamine');
  if fg[fg_enol]                           then mod_wrt_ln('enol');
  if fg[fg_enolether]                      then mod_wrt_ln('enol ether');
//  if fg[fg_hydroxy]                        then mod_wrt_ln('hydroxy compound');
//  if fg[fg_alcohol]                        then mod_wrt_ln('alcohol');
  if fg[fg_prim_alcohol]                   then mod_wrt_ln('primary alcohol');
  if fg[fg_sec_alcohol]                    then mod_wrt_ln('secondary alcohol');
  if fg[fg_tert_alcohol]                   then mod_wrt_ln('tertiary alcohol');
  if fg[fg_1_2_diol]                       then mod_wrt_ln('1,2-diol');
  if fg[fg_1_2_aminoalcohol]               then mod_wrt_ln('1,2-aminoalcohol');
  if fg[fg_phenol]                         then mod_wrt_ln('phenol or hydroxyhetarene');
  if fg[fg_1_2_diphenol]                   then mod_wrt_ln('1,2-diphenol');
  if fg[fg_enediol]                        then mod_wrt_ln('enediol');
//  if fg[fg_ether]                          then mod_wrt_ln('ether');
  if fg[fg_dialkylether]                   then mod_wrt_ln('dialkyl ether');
  if fg[fg_alkylarylether]                 then mod_wrt_ln('alkyl aryl ether ');
  if fg[fg_diarylether]                    then mod_wrt_ln('diaryl ether');
  if fg[fg_thioether]                      then mod_wrt_ln('thioether');
  if fg[fg_disulfide]                      then mod_wrt_ln('disulfide');
  if fg[fg_peroxide]                       then mod_wrt_ln('peroxide');
  if fg[fg_hydroperoxide]                  then mod_wrt_ln('hydroperoxide ');
  if fg[fg_hydrazine]                      then mod_wrt_ln('hydrazine derivative');
  if fg[fg_hydroxylamine]                  then mod_wrt_ln('hydroxylamine');
//  if fg[fg_amine]                          then mod_wrt_ln('amine');
  if fg[fg_prim_amine]                     then mod_wrt_ln('primary amine');
  if fg[fg_prim_aliph_amine]               then mod_wrt_ln('primary aliphatic amine (alkylamine)');
  if fg[fg_prim_arom_amine]                then mod_wrt_ln('primary aromatic amine');
  if fg[fg_sec_amine]                      then mod_wrt_ln('secondary amine');
  if fg[fg_sec_aliph_amine]                then mod_wrt_ln('secondary aliphatic amine (dialkylamine)');
  if fg[fg_sec_mixed_amine]                then mod_wrt_ln('secondary aliphatic/aromatic amine (alkylarylamine)');
  if fg[fg_sec_arom_amine]                 then mod_wrt_ln('secondary aromatic amine (diarylamine)');
  if fg[fg_tert_amine]                     then mod_wrt_ln('tertiary amine');
  if fg[fg_tert_aliph_amine]               then mod_wrt_ln('tertiary aliphatic amine (trialkylamine)');
  if fg[fg_tert_mixed_amine]               then mod_wrt_ln('tertiary aliphatic/aromatic amine (alkylarylamine)');
  if fg[fg_tert_arom_amine]                then mod_wrt_ln('tertiary aromatic amine (triarylamine)');
  if fg[fg_quart_ammonium]                 then mod_wrt_ln('quaternary ammonium salt');
  if fg[fg_n_oxide]                        then mod_wrt_ln('N-oxide');
//  if fg[fg_halogen_deriv]                  then mod_wrt_ln('halogen derivative');
//  if fg[fg_alkyl_halide]                   then mod_wrt_ln('alkyl halide');
  if fg[fg_alkyl_fluoride]                 then mod_wrt_ln('alkyl fluoride');
  if fg[fg_alkyl_chloride]                 then mod_wrt_ln('alkyl chloride');
  if fg[fg_alkyl_bromide]                  then mod_wrt_ln('alkyl bromide');
  if fg[fg_alkyl_iodide]                   then mod_wrt_ln('alkyl iodide');
//  if fg[fg_aryl_halide]                    then mod_wrt_ln('aryl halide');
  if fg[fg_aryl_fluoride]                  then mod_wrt_ln('aryl fluoride');
  if fg[fg_aryl_chloride]                  then mod_wrt_ln('aryl chloride');
  if fg[fg_aryl_bromide]                   then mod_wrt_ln('aryl bromide');
  if fg[fg_aryl_iodide]                    then mod_wrt_ln('aryl iodide');
  if fg[fg_organometallic]                 then mod_wrt_ln('organometallic compound');
  if fg[fg_organolithium]                  then mod_wrt_ln('organolithium compound');
  if fg[fg_organomagnesium]                then mod_wrt_ln('organomagnesium compound');
//  if fg[fg_carboxylic_acid_deriv]          then mod_wrt_ln('carboxylic acid derivative');
  if fg[fg_carboxylic_acid]                then mod_wrt_ln('carboxylic acid');
  if fg[fg_carboxylic_acid_salt]           then mod_wrt_ln('carboxylic acid salt');
  if fg[fg_carboxylic_acid_ester]          then mod_wrt_ln('carboxylic acid ester');
  if fg[fg_lactone]                        then mod_wrt_ln('lactone');
//  if fg[fg_carboxylic_acid_amide]          then mod_wrt_ln('carboxylic acid amide');
  if fg[fg_carboxylic_acid_prim_amide]     then mod_wrt_ln('primary carboxylic acid amide');
  if fg[fg_carboxylic_acid_sec_amide]      then mod_wrt_ln('secondary carboxylic acid amide');
  if fg[fg_carboxylic_acid_tert_amide]     then mod_wrt_ln('tertiary carboxylic acid amide');
  if fg[fg_lactam]                         then mod_wrt_ln('lactam');
  if fg[fg_carboxylic_acid_hydrazide]      then mod_wrt_ln('carboxylic acid hydrazide');
  if fg[fg_carboxylic_acid_azide]          then mod_wrt_ln('carboxylic acid azide');
  if fg[fg_hydroxamic_acid]                then mod_wrt_ln('hydroxamic acid');
  if fg[fg_carboxylic_acid_amidine]        then mod_wrt_ln('carboxylic acid amidine');
  if fg[fg_carboxylic_acid_amidrazone]     then mod_wrt_ln('carboxylic acid amidrazone');
  if fg[fg_nitrile]                        then mod_wrt_ln('carbonitrile');
//  if fg[fg_acyl_halide]                    then mod_wrt_ln('acyl halide');
  if fg[fg_acyl_fluoride]                  then mod_wrt_ln('acyl fluoride');
  if fg[fg_acyl_chloride]                  then mod_wrt_ln('acyl chloride');
  if fg[fg_acyl_bromide]                   then mod_wrt_ln('acyl bromide');
  if fg[fg_acyl_iodide]                    then mod_wrt_ln('acyl iodide');
  if fg[fg_acyl_cyanide]                   then mod_wrt_ln('acyl cyanide');
  if fg[fg_imido_ester]                    then mod_wrt_ln('imido ester');
  if fg[fg_imidoyl_halide]                 then mod_wrt_ln('imidoyl halide');
//  if fg[fg_thiocarboxylic_acid_deriv]      then mod_wrt_ln('thiocarboxylic acid derivative');
  if fg[fg_thiocarboxylic_acid]            then mod_wrt_ln('thiocarboxylic acid');
  if fg[fg_thiocarboxylic_acid_ester]      then mod_wrt_ln('thiocarboxylic acid ester');
  if fg[fg_thiolactone]                    then mod_wrt_ln('thiolactone');
  if fg[fg_thiocarboxylic_acid_amide]      then mod_wrt_ln('thiocarboxylic acid amide');
  if fg[fg_thiolactam]                     then mod_wrt_ln('thiolactam');
  if fg[fg_imido_thioester]                then mod_wrt_ln('imidothioester');
  if fg[fg_oxohetarene]                    then mod_wrt_ln('oxo(het)arene');
  if fg[fg_thioxohetarene]                 then mod_wrt_ln('thioxo(het)arene');
  if fg[fg_iminohetarene]                  then mod_wrt_ln('imino(het)arene');
  if fg[fg_orthocarboxylic_acid_deriv]     then mod_wrt_ln('orthocarboxylic acid derivative');
  if fg[fg_carboxylic_acid_orthoester]     then mod_wrt_ln('orthoester');
  if fg[fg_carboxylic_acid_amide_acetal]   then mod_wrt_ln('amide acetal');
  if fg[fg_carboxylic_acid_anhydride]      then mod_wrt_ln('carboxylic acid anhydride');
//  if fg[fg_carboxylic_acid_imide]          then mod_wrt_ln('carboxylic acid imide');
  if fg[fg_carboxylic_acid_unsubst_imide]  then mod_wrt_ln('carboxylic acid imide, N-unsubstituted');
  if fg[fg_carboxylic_acid_subst_imide]    then mod_wrt_ln('carboxylic acid imide, N-substituted');
  if fg[fg_co2_deriv]                      then mod_wrt_ln('CO2 derivative (general)');
//  if fg[fg_carbonic_acid_deriv]            then mod_wrt_ln('carbonic acid dervivative');
  if fg[fg_carbonic_acid_monoester]        then mod_wrt_ln('carbonic acid monoester');
  if fg[fg_carbonic_acid_diester]          then mod_wrt_ln('carbonic acid diester');
  if fg[fg_carbonic_acid_ester_halide]     then mod_wrt_ln('carbonic acid ester halide (alkyl/aryl haloformate)');
  if fg[fg_thiocarbonic_acid_deriv]        then mod_wrt_ln('thiocarbonic acid derivative');
  if fg[fg_thiocarbonic_acid_monoester]    then mod_wrt_ln('thiocarbonic acid monoester');
  if fg[fg_thiocarbonic_acid_diester]      then mod_wrt_ln('thiocarbonic acid diester');
  if fg[fg_thiocarbonic_acid_ester_halide] then mod_wrt_ln('thiocarbonic acid halide (alkyl/aryl halothioformate)');
//  if fg[fg_carbamic_acid_deriv]            then mod_wrt_ln('carbamic acid derivative');
  if fg[fg_carbamic_acid]                  then mod_wrt_ln('carbamic acid');
  if fg[fg_carbamic_acid_ester]            then mod_wrt_ln('carbamic acid ester (urethane)');
  if fg[fg_carbamic_acid_halide]           then mod_wrt_ln('carbamic acid halide (haloformic acid amide)');
//  if fg[fg_thiocarbamic_acid_deriv]        then mod_wrt_ln('thiocarbamic acid derivative');
  if fg[fg_thiocarbamic_acid]              then mod_wrt_ln('thiocarbamic acid');
  if fg[fg_thiocarbamic_acid_ester]        then mod_wrt_ln('thiocarbamic acid ester');
  if fg[fg_thiocarbamic_acid_halide]       then mod_wrt_ln('thiocarbamic acid halide (halothioformic acid amide)');
  if fg[fg_urea]                           then mod_wrt_ln('urea');
  if fg[fg_isourea]                        then mod_wrt_ln('isourea');
  if fg[fg_thiourea]                       then mod_wrt_ln('thiourea');
  if fg[fg_isothiourea]                    then mod_wrt_ln('isothiourea');
  if fg[fg_guanidine]                      then mod_wrt_ln('guanidine');
  if fg[fg_semicarbazide]                  then mod_wrt_ln('semicarbazide');
  if fg[fg_thiosemicarbazide]              then mod_wrt_ln('thiosemicarbazide');
  if fg[fg_azide]                          then mod_wrt_ln('azide');
  if fg[fg_azo_compound]                   then mod_wrt_ln('azo compound');
  if fg[fg_diazonium_salt]                 then mod_wrt_ln('diazonium salt');
  if fg[fg_isonitrile]                     then mod_wrt_ln('isonitrile');
  if fg[fg_cyanate]                        then mod_wrt_ln('cyanate');
  if fg[fg_isocyanate]                     then mod_wrt_ln('isocyanate');
  if fg[fg_thiocyanate]                    then mod_wrt_ln('thiocyanate');
  if fg[fg_isothiocyanate]                 then mod_wrt_ln('isothiocyanate');
  if fg[fg_carbodiimide]                   then mod_wrt_ln('carbodiimide');
  if fg[fg_nitroso_compound]               then mod_wrt_ln('nitroso compound');
  if fg[fg_nitro_compound]                 then mod_wrt_ln('nitro compound');
  if fg[fg_nitrite]                        then mod_wrt_ln('nitrite');
  if fg[fg_nitrate]                        then mod_wrt_ln('nitrate');
//  if fg[fg_sulfuric_acid_deriv]            then mod_wrt_ln('sulfuric acid derivative');
  if fg[fg_sulfuric_acid]                  then mod_wrt_ln('sulfuric acid');
  if fg[fg_sulfuric_acid_monoester]        then mod_wrt_ln('sulfuric acid monoester');
  if fg[fg_sulfuric_acid_diester]          then mod_wrt_ln('sulfuric acid diester');
  if fg[fg_sulfuric_acid_amide_ester]      then mod_wrt_ln('sulfuric acid amide ester');
  if fg[fg_sulfuric_acid_amide]            then mod_wrt_ln('sulfuric acid amide');
  if fg[fg_sulfuric_acid_diamide]          then mod_wrt_ln('sulfuric acid diamide');
  if fg[fg_sulfuryl_halide]                then mod_wrt_ln('sulfuryl halide');
//  if fg[fg_sulfonic_acid_deriv]            then mod_wrt_ln('sulfonic acid derivative ');
  if fg[fg_sulfonic_acid]                  then mod_wrt_ln('sulfonic acid');
  if fg[fg_sulfonic_acid_ester]            then mod_wrt_ln('sulfonic acid ester');
  if fg[fg_sulfonamide]                    then mod_wrt_ln('sulfonamide');
  if fg[fg_sulfonyl_halide]                then mod_wrt_ln('sulfonyl halide');
  if fg[fg_sulfone]                        then mod_wrt_ln('sulfone');
  if fg[fg_sulfoxide]                      then mod_wrt_ln('sulfoxide');
//  if fg[fg_sulfinic_acid_deriv]            then mod_wrt_ln('sulfinic acid derivative');
  if fg[fg_sulfinic_acid]                  then mod_wrt_ln('sulfinic acid');
  if fg[fg_sulfinic_acid_ester]            then mod_wrt_ln('sulfinic acid ester');
  if fg[fg_sulfinic_acid_halide]           then mod_wrt_ln('sulfinic acid halide');
  if fg[fg_sulfinic_acid_amide]            then mod_wrt_ln('sulfinic acid amide');
//  if fg[fg_sulfenic_acid_deriv]            then mod_wrt_ln('sulfenic acid derivative');
  if fg[fg_sulfenic_acid]                  then mod_wrt_ln('sulfenic acid');
  if fg[fg_sulfenic_acid_ester]            then mod_wrt_ln('sulfenic acid ester');
  if fg[fg_sulfenic_acid_halide]           then mod_wrt_ln('sulfenic acid halide');
  if fg[fg_sulfenic_acid_amide]            then mod_wrt_ln('sulfenic acid amide');
  if fg[fg_thiol]                          then mod_wrt_ln('thiol (sulfanyl compound)');
  if fg[fg_alkylthiol]                     then mod_wrt_ln('alkylthiol');
  if fg[fg_arylthiol]                      then mod_wrt_ln('arylthiol');
//  if fg[fg_phosphoric_acid_deriv]          then mod_wrt_ln('phosphoric acid derivative');
  if fg[fg_phosphoric_acid]                then mod_wrt_ln('phosphoric acid');
  if fg[fg_phosphoric_acid_ester]          then mod_wrt_ln('phosphoric acid ester');
  if fg[fg_phosphoric_acid_halide]         then mod_wrt_ln('phosphoric acid halide');
  if fg[fg_phosphoric_acid_amide]          then mod_wrt_ln('phosphoric acid amide');
//  if fg[fg_thiophosphoric_acid_deriv]      then mod_wrt_ln('thiophosphoric acid derivative');
  if fg[fg_thiophosphoric_acid]            then mod_wrt_ln('thiophosphoric acid');
  if fg[fg_thiophosphoric_acid_ester]      then mod_wrt_ln('thiophosphoric acid ester');
  if fg[fg_thiophosphoric_acid_halide]     then mod_wrt_ln('thiophosphoric acid halide');
  if fg[fg_thiophosphoric_acid_amide]      then mod_wrt_ln('thiophosphoric acid amide');
  if fg[fg_phosphonic_acid_deriv]          then mod_wrt_ln('phosphonic acid derivative ');
  if fg[fg_phosphonic_acid]                then mod_wrt_ln('phosphonic acid');
  if fg[fg_phosphonic_acid_ester]          then mod_wrt_ln('phosphonic acid ester');
  if fg[fg_phosphine]                      then mod_wrt_ln('phosphine');
  if fg[fg_phosphinoxide]                  then mod_wrt_ln('phosphine oxide');
  if fg[fg_boronic_acid_deriv]             then mod_wrt_ln('boronic acid derivative');
  if fg[fg_boronic_acid]                   then mod_wrt_ln('boronic acid');
  if fg[fg_boronic_acid_ester]             then mod_wrt_ln('boronic acid ester');
  if fg[fg_alkene]                         then mod_wrt_ln('alkene');
  if fg[fg_alkyne]                         then mod_wrt_ln('alkyne');
  if fg[fg_aromatic]                       then mod_wrt_ln('aromatic compound');
  if fg[fg_heterocycle]                    then mod_wrt_ln('heterocyclic compound');
  if fg[fg_alpha_aminoacid]                then mod_wrt_ln('alpha-aminoacid');
  if fg[fg_alpha_hydroxyacid]              then mod_wrt_ln('alpha-hydroxyacid');
end;


procedure mod_wrt_noln_fg_text_de;
begin
  if fg[fg_cation]                         then mod_wrt_ln('Kation');
  if fg[fg_anion]                          then mod_wrt_ln('Anion');
//  if fg[fg_carbonyl]                       then mod_wrt_ln('Carbonylverbindung');
  if fg[fg_aldehyde]                       then mod_wrt_ln('Aldehyd');
  if fg[fg_ketone]                         then mod_wrt_ln('Keton');
//  if fg[fg_thiocarbonyl]                   then mod_wrt_ln('Thiocarbonylverbindung');
  if fg[fg_thioaldehyde]                   then mod_wrt_ln('Thioaldehyd');
  if fg[fg_thioketone]                     then mod_wrt_ln('Thioketon');
  if fg[fg_imine]                          then mod_wrt_ln('Imin');
  if fg[fg_hydrazone]                      then mod_wrt_ln('Hydrazon');
  if fg[fg_semicarbazone]                  then mod_wrt_ln('Semicarbazon');
  if fg[fg_thiosemicarbazone]              then mod_wrt_ln('Thiosemicarbazon');
  if fg[fg_oxime]                          then mod_wrt_ln('Oxim');
  if fg[fg_oxime_ether]                    then mod_wrt_ln('Oximether');
  if fg[fg_ketene]                         then mod_wrt_ln('Keten');
  if fg[fg_ketene_acetal_deriv]            then mod_wrt_ln('Keten-Acetal oder Derivat');
  if fg[fg_carbonyl_hydrate]               then mod_wrt_ln('Carbonyl-Hydrat');
  if fg[fg_hemiacetal]                     then mod_wrt_ln('Halbacetal');
  if fg[fg_acetal]                         then mod_wrt_ln('Acetal');
  if fg[fg_hemiaminal]                     then mod_wrt_ln('Halbaminal');
  if fg[fg_aminal]                         then mod_wrt_ln('Aminal');
  if fg[fg_thiohemiaminal]                 then mod_wrt_ln('Thiohalbaminal');
  if fg[fg_thioacetal]                     then mod_wrt_ln('Thioacetal');
  if fg[fg_enamine]                        then mod_wrt_ln('Enamin');
  if fg[fg_enol]                           then mod_wrt_ln('Enol');
  if fg[fg_enolether]                      then mod_wrt_ln('Enolether');
//  if fg[fg_hydroxy]                        then mod_wrt_ln('Hydroxy-Verbindung');
//  if fg[fg_alcohol]                        then mod_wrt_ln('Alkohol');
  if fg[fg_prim_alcohol]                   then mod_wrt_ln('primärer Alkohol');
  if fg[fg_sec_alcohol]                    then mod_wrt_ln('sekundärer Alkohol');
  if fg[fg_tert_alcohol]                   then mod_wrt_ln('tertiärer Alkohol');
  if fg[fg_1_2_diol]                       then mod_wrt_ln('1,2-Diol');
  if fg[fg_1_2_aminoalcohol]               then mod_wrt_ln('1,2-Aminoalkohol');
  if fg[fg_phenol]                         then mod_wrt_ln('Phenol oder Hydroxyhetaren');
  if fg[fg_1_2_diphenol]                   then mod_wrt_ln('1,2-Diphenol');
  if fg[fg_enediol]                        then mod_wrt_ln('Endiol');
//  if fg[fg_ether]                          then mod_wrt_ln('Ether');
  if fg[fg_dialkylether]                   then mod_wrt_ln('Dialkylether');
  if fg[fg_alkylarylether]                 then mod_wrt_ln('Alkylarylether ');
  if fg[fg_diarylether]                    then mod_wrt_ln('Diarylether');
  if fg[fg_thioether]                      then mod_wrt_ln('Thioether');
  if fg[fg_disulfide]                      then mod_wrt_ln('Disulfid');
  if fg[fg_peroxide]                       then mod_wrt_ln('Peroxid');
  if fg[fg_hydroperoxide]                  then mod_wrt_ln('Hydroperoxid');
  if fg[fg_hydrazine]                      then mod_wrt_ln('Hydrazin-Derivat');
  if fg[fg_hydroxylamine]                  then mod_wrt_ln('Hydroxylamin');
//  if fg[fg_amine]                          then mod_wrt_ln('Amin');
  if fg[fg_prim_amine]                     then mod_wrt_ln('primäres Amin');
  if fg[fg_prim_aliph_amine]               then mod_wrt_ln('primäres aliphatisches Amin (Alkylamin)');
  if fg[fg_prim_arom_amine]                then mod_wrt_ln('primäres aromatisches Amin');
  if fg[fg_sec_amine]                      then mod_wrt_ln('sekundäres Amin');
  if fg[fg_sec_aliph_amine]                then mod_wrt_ln('sekundäres aliphatisches Amin (Dialkylamin)');
  if fg[fg_sec_mixed_amine]                then mod_wrt_ln('sekundäres aliphatisches/aromatisches Amin (Alkylarylamin)');
  if fg[fg_sec_arom_amine]                 then mod_wrt_ln('sekundäres aromatisches Amin (Diarylamin)');
  if fg[fg_tert_amine]                     then mod_wrt_ln('tertiäres Amin');
  if fg[fg_tert_aliph_amine]               then mod_wrt_ln('tertiäres aliphatisches Amin (Trialkylamin)');
  if fg[fg_tert_mixed_amine]               then mod_wrt_ln('tertiäres aliphatisches/aromatisches Amin (Alkylarylamin)');
  if fg[fg_tert_arom_amine]                then mod_wrt_ln('tertiäres aromatisches Amin (Triarylamin)');
  if fg[fg_quart_ammonium]                 then mod_wrt_ln('quartäres Ammoniumsalz');
  if fg[fg_n_oxide]                        then mod_wrt_ln('N-Oxid');
//  if fg[fg_halogen_deriv]                  then mod_wrt_ln('Halogenverbindung');
//  if fg[fg_alkyl_halide]                   then mod_wrt_ln('Alkylhalogenid');
  if fg[fg_alkyl_fluoride]                 then mod_wrt_ln('Alkylfluorid');
  if fg[fg_alkyl_chloride]                 then mod_wrt_ln('Alkylchlorid');
  if fg[fg_alkyl_bromide]                  then mod_wrt_ln('Alkylbromid');
  if fg[fg_alkyl_iodide]                   then mod_wrt_ln('Alkyliodid');
//  if fg[fg_aryl_halide]                    then mod_wrt_ln('Arylhalogenid');
  if fg[fg_aryl_fluoride]                  then mod_wrt_ln('Arylfluorid');
  if fg[fg_aryl_chloride]                  then mod_wrt_ln('Arylchlorid');
  if fg[fg_aryl_bromide]                   then mod_wrt_ln('Arylbromid');
  if fg[fg_aryl_iodide]                    then mod_wrt_ln('Aryliodid');
  if fg[fg_organometallic]                 then mod_wrt_ln('Organometall-Verbindung');
  if fg[fg_organolithium]                  then mod_wrt_ln('Organolithium-Verbindung');
  if fg[fg_organomagnesium]                then mod_wrt_ln('Organomagnesium-Verbindung');
//  if fg[fg_carboxylic_acid_deriv]          then mod_wrt_ln('Carbonsäure-Derivat');
  if fg[fg_carboxylic_acid]                then mod_wrt_ln('Carbonsäure');
  if fg[fg_carboxylic_acid_salt]           then mod_wrt_ln('Carbonsäuresalz');
  if fg[fg_carboxylic_acid_ester]          then mod_wrt_ln('Carbonsäureester');
  if fg[fg_lactone]                        then mod_wrt_ln('Lacton');
//  if fg[fg_carboxylic_acid_amide]          then mod_wrt_ln('Carbonsäureamid');
  if fg[fg_carboxylic_acid_prim_amide]     then mod_wrt_ln('primäres Carbonsäureamid');
  if fg[fg_carboxylic_acid_sec_amide]      then mod_wrt_ln('sekundäres Carbonsäureamid');
  if fg[fg_carboxylic_acid_tert_amide]     then mod_wrt_ln('tertiäres Carbonsäureamid');
  if fg[fg_lactam]                         then mod_wrt_ln('Lactam');
  if fg[fg_carboxylic_acid_hydrazide]      then mod_wrt_ln('Carbonsäurehydrazid');
  if fg[fg_carboxylic_acid_azide]          then mod_wrt_ln('Carbonsäureazid');
  if fg[fg_hydroxamic_acid]                then mod_wrt_ln('Hydroxamsäure');
  if fg[fg_carboxylic_acid_amidine]        then mod_wrt_ln('Carbonsäureamidin');
  if fg[fg_carboxylic_acid_amidrazone]     then mod_wrt_ln('Carbonsäureamidrazon');
  if fg[fg_nitrile]                        then mod_wrt_ln('Carbonitril');
//  if fg[fg_acyl_halide]                    then mod_wrt_ln('Acylhalogenid');
  if fg[fg_acyl_fluoride]                  then mod_wrt_ln('Acylfluorid');
  if fg[fg_acyl_chloride]                  then mod_wrt_ln('Acylchlorid');
  if fg[fg_acyl_bromide]                   then mod_wrt_ln('Acylbromid');
  if fg[fg_acyl_iodide]                    then mod_wrt_ln('Acyliodid');
  if fg[fg_acyl_cyanide]                   then mod_wrt_ln('Acylcyanid');
  if fg[fg_imido_ester]                    then mod_wrt_ln('Imidoester');
  if fg[fg_imidoyl_halide]                 then mod_wrt_ln('Imidoylhalogenid');
//  if fg[fg_thiocarboxylic_acid_deriv]      then mod_wrt_ln('Thiocarbonsäure-Derivat');
  if fg[fg_thiocarboxylic_acid]            then mod_wrt_ln('Thiocarbonsäure');
  if fg[fg_thiocarboxylic_acid_ester]      then mod_wrt_ln('Thiocarbonsäureester');
  if fg[fg_thiolactone]                    then mod_wrt_ln('Thiolacton');
  if fg[fg_thiocarboxylic_acid_amide]      then mod_wrt_ln('Thiocarbonsäureamid');
  if fg[fg_thiolactam]                     then mod_wrt_ln('Thiolactam');
  if fg[fg_imido_thioester]                then mod_wrt_ln('Imidothioester');
  if fg[fg_oxohetarene]                    then mod_wrt_ln('Oxo(het)aren');
  if fg[fg_thioxohetarene]                 then mod_wrt_ln('Thioxo(het)aren');
  if fg[fg_iminohetarene]                  then mod_wrt_ln('Imino(het)aren');
  if fg[fg_orthocarboxylic_acid_deriv]     then mod_wrt_ln('Orthocarbonsäure-Derivat');
  if fg[fg_carboxylic_acid_orthoester]     then mod_wrt_ln('Orthoester');
  if fg[fg_carboxylic_acid_amide_acetal]   then mod_wrt_ln('Amidacetal');
  if fg[fg_carboxylic_acid_anhydride]      then mod_wrt_ln('Carbonsäureanhydrid');
//  if fg[fg_carboxylic_acid_imide]          then mod_wrt_ln('Carbonsäureimid');
  if fg[fg_carboxylic_acid_unsubst_imide]  then mod_wrt_ln('Carbonsäureimid, N-unsubstituiert');
  if fg[fg_carboxylic_acid_subst_imide]    then mod_wrt_ln('Carbonsäureimid, N-substituiert');
  if fg[fg_co2_deriv]                      then mod_wrt_ln('CO2-Derivat (allgemein)');
//  if fg[fg_carbonic_acid_deriv]            then mod_wrt_ln('Kohlensäure-Dervivat');
  if fg[fg_carbonic_acid_monoester]        then mod_wrt_ln('Kohlensäuremonoester');
  if fg[fg_carbonic_acid_diester]          then mod_wrt_ln('Kohlensäurediester');
  if fg[fg_carbonic_acid_ester_halide]     then mod_wrt_ln('Kohlensäureesterhalogenid (Alkyl/Aryl-Halogenformiat)');
  if fg[fg_thiocarbonic_acid_deriv]        then mod_wrt_ln('Thiokohlensäure-Derivat');
  if fg[fg_thiocarbonic_acid_monoester]    then mod_wrt_ln('Thiokohlensäuremonoester');
  if fg[fg_thiocarbonic_acid_diester]      then mod_wrt_ln('Thiokohlensäurediester');
  if fg[fg_thiocarbonic_acid_ester_halide] then mod_wrt_ln('Thiokohlensäurehalogenid (Alkyl/Aryl-Halogenthioformiat)');
//  if fg[fg_carbamic_acid_deriv]            then mod_wrt_ln('Carbaminsäure-Derivat');
  if fg[fg_carbamic_acid]                  then mod_wrt_ln('Carbaminsäure');
  if fg[fg_carbamic_acid_ester]            then mod_wrt_ln('Carbaminsäureester (Urethan)');
  if fg[fg_carbamic_acid_halide]           then mod_wrt_ln('Carbaminsäurehalogenid (Halogenformamid)');
//  if fg[fg_thiocarbamic_acid_deriv]        then mod_wrt_ln('Thiocarbaminsäure-Derivat');
  if fg[fg_thiocarbamic_acid]              then mod_wrt_ln('Thiocarbaminsäure');
  if fg[fg_thiocarbamic_acid_ester]        then mod_wrt_ln('Thiocarbaminsäureester');
  if fg[fg_thiocarbamic_acid_halide]       then mod_wrt_ln('Thiocarbaminsäurehalogenid (Halogenthioformamid)');
  if fg[fg_urea]                           then mod_wrt_ln('Harnstoff');
  if fg[fg_isourea]                        then mod_wrt_ln('Isoharnstoff');
  if fg[fg_thiourea]                       then mod_wrt_ln('Thioharnstoff');
  if fg[fg_isothiourea]                    then mod_wrt_ln('Isothioharnstoff');
  if fg[fg_guanidine]                      then mod_wrt_ln('Guanidin');
  if fg[fg_semicarbazide]                  then mod_wrt_ln('Semicarbazid');
  if fg[fg_thiosemicarbazide]              then mod_wrt_ln('Thiosemicarbazid');
  if fg[fg_azide]                          then mod_wrt_ln('Azid');
  if fg[fg_azo_compound]                   then mod_wrt_ln('Azoverbindung');
  if fg[fg_diazonium_salt]                 then mod_wrt_ln('Diazoniumsalz');
  if fg[fg_isonitrile]                     then mod_wrt_ln('Isonitril');
  if fg[fg_cyanate]                        then mod_wrt_ln('Cyanat');
  if fg[fg_isocyanate]                     then mod_wrt_ln('Isocyanat');
  if fg[fg_thiocyanate]                    then mod_wrt_ln('Thiocyanat');
  if fg[fg_isothiocyanate]                 then mod_wrt_ln('Isothiocyanat');
  if fg[fg_carbodiimide]                   then mod_wrt_ln('Carbodiimid');
  if fg[fg_nitroso_compound]               then mod_wrt_ln('Nitroso-Verbindung');
  if fg[fg_nitro_compound]                 then mod_wrt_ln('Nitro-Verbindung');
  if fg[fg_nitrite]                        then mod_wrt_ln('Nitrit');
  if fg[fg_nitrate]                        then mod_wrt_ln('Nitrat');
//  if fg[fg_sulfuric_acid_deriv]            then mod_wrt_ln('Schwefelsäure-Derivat');
  if fg[fg_sulfuric_acid]                  then mod_wrt_ln('Schwefelsäure');
  if fg[fg_sulfuric_acid_monoester]        then mod_wrt_ln('Schwefelsäuremonoester');
  if fg[fg_sulfuric_acid_diester]          then mod_wrt_ln('Schwefelsäurediester');
  if fg[fg_sulfuric_acid_amide_ester]      then mod_wrt_ln('Schwefelsäureamidester');
  if fg[fg_sulfuric_acid_amide]            then mod_wrt_ln('Schwefelsäureamid');
  if fg[fg_sulfuric_acid_diamide]          then mod_wrt_ln('Schwefelsäurediamid');
  if fg[fg_sulfuryl_halide]                then mod_wrt_ln('Sulfurylhalogenid');
//  if fg[fg_sulfonic_acid_deriv]            then mod_wrt_ln('Sulfonsäure-Derivat ');
  if fg[fg_sulfonic_acid]                  then mod_wrt_ln('Sulfonsäure');
  if fg[fg_sulfonic_acid_ester]            then mod_wrt_ln('Sulfonsäureester');
  if fg[fg_sulfonamide]                    then mod_wrt_ln('Sulfonamid');
  if fg[fg_sulfonyl_halide]                then mod_wrt_ln('Sulfonylhalogenid');
  if fg[fg_sulfone]                        then mod_wrt_ln('Sulfon');
  if fg[fg_sulfoxide]                      then mod_wrt_ln('Sulfoxid');
//  if fg[fg_sulfinic_acid_deriv]            then mod_wrt_ln('Sulfinsäure-Derivat');
  if fg[fg_sulfinic_acid]                  then mod_wrt_ln('Sulfinsäure');
  if fg[fg_sulfinic_acid_ester]            then mod_wrt_ln('Sulfinsäureester');
  if fg[fg_sulfinic_acid_halide]           then mod_wrt_ln('Sulfinsäurehalogenid');
  if fg[fg_sulfinic_acid_amide]            then mod_wrt_ln('Sulfinsäureamid');
//  if fg[fg_sulfenic_acid_deriv]            then mod_wrt_ln('Sulfensäure-Derivat');
  if fg[fg_sulfenic_acid]                  then mod_wrt_ln('Sulfensäure');
  if fg[fg_sulfenic_acid_ester]            then mod_wrt_ln('Sulfensäureester');
  if fg[fg_sulfenic_acid_halide]           then mod_wrt_ln('Sulfensäurehalogenid');
  if fg[fg_sulfenic_acid_amide]            then mod_wrt_ln('Sulfensäureamid');
  if fg[fg_thiol]                          then mod_wrt_ln('Thiol (Sulfanyl-Verbindung, Mercaptan)');
  if fg[fg_alkylthiol]                     then mod_wrt_ln('Alkylthiol');
  if fg[fg_arylthiol]                      then mod_wrt_ln('Arylthiol');
//  if fg[fg_phosphoric_acid_deriv]          then mod_wrt_ln('Phosphorsäure-Derivat');
  if fg[fg_phosphoric_acid]                then mod_wrt_ln('Phosphorsäure');
  if fg[fg_phosphoric_acid_ester]          then mod_wrt_ln('Phosphorsäureester');
  if fg[fg_phosphoric_acid_halide]         then mod_wrt_ln('Phosphorsäurehalogenid');
  if fg[fg_phosphoric_acid_amide]          then mod_wrt_ln('Phosphorsäureamid');
//  if fg[fg_thiophosphoric_acid_deriv]      then mod_wrt_ln('Thiophosphorsäure-Derivat');
  if fg[fg_thiophosphoric_acid]            then mod_wrt_ln('Thiophosphorsäure');
  if fg[fg_thiophosphoric_acid_ester]      then mod_wrt_ln('Thiophosphorsäureester');
  if fg[fg_thiophosphoric_acid_halide]     then mod_wrt_ln('Thiophosphorsäurehalogenid');
  if fg[fg_thiophosphoric_acid_amide]      then mod_wrt_ln('Thiophosphorsäureamid');
  if fg[fg_phosphonic_acid_deriv]          then mod_wrt_ln('Phosphonsäure-Derivat ');
  if fg[fg_phosphonic_acid]                then mod_wrt_ln('Phosphonsäure');
  if fg[fg_phosphonic_acid_ester]          then mod_wrt_ln('Phosphonsäureester');
  if fg[fg_phosphine]                      then mod_wrt_ln('Phosphin');
  if fg[fg_phosphinoxide]                  then mod_wrt_ln('Phosphinoxid');
  if fg[fg_boronic_acid_deriv]             then mod_wrt_ln('Boronsäure-Derivat');
  if fg[fg_boronic_acid]                   then mod_wrt_ln('Boronsäure');
  if fg[fg_boronic_acid_ester]             then mod_wrt_ln('Boronsäureester');
  if fg[fg_alkene]                         then mod_wrt_ln('Alken');
  if fg[fg_alkyne]                         then mod_wrt_ln('Alkin');
  if fg[fg_aromatic]                       then mod_wrt_ln('aromatische Verbindung');
  if fg[fg_heterocycle]                    then mod_wrt_ln('heterocyclische Verbindung');
  if fg[fg_alpha_aminoacid]                then mod_wrt_ln('alpha-Aminosäure');
  if fg[fg_alpha_hydroxyacid]              then mod_wrt_ln('alpha-Hydroxysäure');
end;


procedure mod_wrt_noln_fg_code;
const
  sc = ';';
begin
  if fg[fg_cation]                         then mod_wrt_noln('000000T2',sc);
  if fg[fg_anion]                          then mod_wrt_noln('000000T1',sc);
//  if fg[fg_carbonyl]                       then mod_wrt_noln('C2O10000',sc);
  if fg[fg_aldehyde]                       then mod_wrt_noln('C2O1H000',sc);
  if fg[fg_ketone]                         then mod_wrt_noln('C2O1C000',sc);
//  if fg[fg_thiocarbonyl]                   then mod_wrt_noln('C2S10000',sc);
  if fg[fg_thioaldehyde]                   then mod_wrt_noln('C2S1H000',sc);
  if fg[fg_thioketone]                     then mod_wrt_noln('C2S1C000',sc);
  if fg[fg_imine]                          then mod_wrt_noln('C2N10000',sc);
  if fg[fg_hydrazone]                      then mod_wrt_noln('C2N1N000',sc);
  if fg[fg_semicarbazone]                  then mod_wrt_noln('C2NNC4ON',sc);
  if fg[fg_thiosemicarbazone]              then mod_wrt_noln('C2NNC4SN',sc);
  if fg[fg_oxime]                          then mod_wrt_noln('C2N1OH00',sc);
  if fg[fg_oxime_ether]                    then mod_wrt_noln('C2N1OC00',sc);
  if fg[fg_ketene]                         then mod_wrt_noln('C3OC0000',sc);
  if fg[fg_ketene_acetal_deriv]            then mod_wrt_noln('C3OCC000',sc);
  if fg[fg_carbonyl_hydrate]               then mod_wrt_noln('C2O2H200',sc);
  if fg[fg_hemiacetal]                     then mod_wrt_noln('C2O2HC00',sc);
  if fg[fg_acetal]                         then mod_wrt_noln('C2O2CC00',sc);
  if fg[fg_hemiaminal]                     then mod_wrt_noln('C2NOHC10',sc);
  if fg[fg_aminal]                         then mod_wrt_noln('C2N2CC10',sc);
  if fg[fg_thiohemiaminal]                 then mod_wrt_noln('C2NSHC10',sc);
  if fg[fg_thioacetal]                     then mod_wrt_noln('C2S2CC00',sc);
  if fg[fg_enamine]                        then mod_wrt_noln('C2CNH000',sc);
  if fg[fg_enol]                           then mod_wrt_noln('C2COH000',sc);
  if fg[fg_enolether]                      then mod_wrt_noln('C2COC000',sc);
//  if fg[fg_hydroxy]                        then mod_wrt_noln('O1H00000',sc);
//  if fg[fg_alcohol]                        then mod_wrt_noln('O1H0C000',sc);
  if fg[fg_prim_alcohol]                   then mod_wrt_noln('O1H1C000',sc);
  if fg[fg_sec_alcohol]                    then mod_wrt_noln('O1H2C000',sc);
  if fg[fg_tert_alcohol]                   then mod_wrt_noln('O1H3C000',sc);
  if fg[fg_1_2_diol]                       then mod_wrt_noln('O1H0CO1H',sc);
  if fg[fg_1_2_aminoalcohol]               then mod_wrt_noln('O1H0CN1C',sc);
  if fg[fg_phenol]                         then mod_wrt_noln('O1H1A000',sc);
  if fg[fg_1_2_diphenol]                   then mod_wrt_noln('O1H2A000',sc);
  if fg[fg_enediol]                        then mod_wrt_noln('C2COH200',sc);
//  if fg[fg_ether]                          then mod_wrt_noln('O1C00000',sc);
  if fg[fg_dialkylether]                   then mod_wrt_noln('O1C0CC00',sc);
  if fg[fg_alkylarylether]                 then mod_wrt_noln('O1C0CA00',sc);
  if fg[fg_diarylether]                    then mod_wrt_noln('O1C0AA00',sc);
  if fg[fg_thioether]                      then mod_wrt_noln('S1C00000',sc);
  if fg[fg_disulfide]                      then mod_wrt_noln('S1S1C000',sc);
  if fg[fg_peroxide]                       then mod_wrt_noln('O1O1C000',sc);
  if fg[fg_hydroperoxide]                  then mod_wrt_noln('O1O1H000',sc);
  if fg[fg_hydrazine]                      then mod_wrt_noln('N1N10000',sc);
  if fg[fg_hydroxylamine]                  then mod_wrt_noln('N1O1H000',sc);
//  if fg[fg_amine]                          then mod_wrt_noln('N1C00000',sc);
//  if fg[fg_prim_amine]                     then mod_wrt_noln('N1C10000',sc);
  if fg[fg_prim_aliph_amine]               then mod_wrt_noln('N1C1C000',sc);
  if fg[fg_prim_arom_amine]                then mod_wrt_noln('N1C1A000',sc);
//  if fg[fg_sec_amine]                      then mod_wrt_noln('N1C20000',sc);
  if fg[fg_sec_aliph_amine]                then mod_wrt_noln('N1C2CC00',sc);
  if fg[fg_sec_mixed_amine]                then mod_wrt_noln('N1C2AC00',sc);
  if fg[fg_sec_arom_amine]                 then mod_wrt_noln('N1C2AA00',sc);
//  if fg[fg_tert_amine]                     then mod_wrt_noln('N1C30000',sc);
  if fg[fg_tert_aliph_amine]               then mod_wrt_noln('N1C3CC00',sc);
  if fg[fg_tert_mixed_amine]               then mod_wrt_noln('N1C3AC00',sc);
  if fg[fg_tert_arom_amine]                then mod_wrt_noln('N1C3AA00',sc);
  if fg[fg_quart_ammonium]                 then mod_wrt_noln('N1C400T2',sc);
  if fg[fg_n_oxide]                        then mod_wrt_noln('N0O10000',sc);
//  if fg[fg_halogen_deriv]                  then mod_wrt_noln('XX000000',sc);
//  if fg[fg_alkyl_halide]                   then mod_wrt_noln('XX00C000',sc);
  if fg[fg_alkyl_fluoride]                 then mod_wrt_noln('XF00C000',sc);
  if fg[fg_alkyl_chloride]                 then mod_wrt_noln('XC00C000',sc);
  if fg[fg_alkyl_bromide]                  then mod_wrt_noln('XB00C000',sc);
  if fg[fg_alkyl_iodide]                   then mod_wrt_noln('XI00C000',sc);
//  if fg[fg_aryl_halide]                    then mod_wrt_noln('XX00A000',sc);
  if fg[fg_aryl_fluoride]                  then mod_wrt_noln('XF00A000',sc);
  if fg[fg_aryl_chloride]                  then mod_wrt_noln('XC00A000',sc);
  if fg[fg_aryl_bromide]                   then mod_wrt_noln('XB00A000',sc);
  if fg[fg_aryl_iodide]                    then mod_wrt_noln('XI00A000',sc);
  if fg[fg_organometallic]                 then mod_wrt_noln('000000MX',sc);
  if fg[fg_organolithium]                  then mod_wrt_noln('000000ML',sc);
  if fg[fg_organomagnesium]                then mod_wrt_noln('000000MM',sc);
//  if fg[fg_carboxylic_acid_deriv]          then mod_wrt_noln('C3O20000',sc);
  if fg[fg_carboxylic_acid]                then mod_wrt_noln('C3O2H000',sc);
  if fg[fg_carboxylic_acid_salt]           then mod_wrt_noln('C3O200T1',sc);
  if fg[fg_carboxylic_acid_ester]          then mod_wrt_noln('C3O2C000',sc);
  if fg[fg_lactone]                        then mod_wrt_noln('C3O2CZ00',sc);
//  if fg[fg_carboxylic_acid_amide]          then mod_wrt_noln('C3ONC000',sc);
  if fg[fg_carboxylic_acid_prim_amide]     then mod_wrt_noln('C3ONC100',sc);
  if fg[fg_carboxylic_acid_sec_amide]      then mod_wrt_noln('C3ONC200',sc);
  if fg[fg_carboxylic_acid_tert_amide]     then mod_wrt_noln('C3ONC300',sc);
  if fg[fg_lactam]                         then mod_wrt_noln('C3ONCZ00',sc);
  if fg[fg_carboxylic_acid_hydrazide]      then mod_wrt_noln('C3ONN100',sc);
  if fg[fg_carboxylic_acid_azide]          then mod_wrt_noln('C3ONN200',sc);
  if fg[fg_hydroxamic_acid]                then mod_wrt_noln('C3ONOH00',sc);
  if fg[fg_carboxylic_acid_amidine]        then mod_wrt_noln('C3N2H000',sc);
  if fg[fg_carboxylic_acid_amidrazone]     then mod_wrt_noln('C3NNN100',sc);
  if fg[fg_nitrile]                        then mod_wrt_noln('C3N00000',sc);
//  if fg[fg_acyl_halide]                    then mod_wrt_noln('C3OXX000',sc);
  if fg[fg_acyl_fluoride]                  then mod_wrt_noln('C3OXF000',sc);
  if fg[fg_acyl_chloride]                  then mod_wrt_noln('C3OXC000',sc);
  if fg[fg_acyl_bromide]                   then mod_wrt_noln('C3OXB000',sc);
  if fg[fg_acyl_iodide]                    then mod_wrt_noln('C3OXI000',sc);
  if fg[fg_acyl_cyanide]                   then mod_wrt_noln('C2OC3N00',sc);
  if fg[fg_imido_ester]                    then mod_wrt_noln('C3NOC000',sc);
  if fg[fg_imidoyl_halide]                 then mod_wrt_noln('C3NXX000',sc);
//  if fg[fg_thiocarboxylic_acid_deriv]      then mod_wrt_noln('C3SO0000',sc);
  if fg[fg_thiocarboxylic_acid]            then mod_wrt_noln('C3SOH000',sc);
  if fg[fg_thiocarboxylic_acid_ester]      then mod_wrt_noln('C3SOC000',sc);
  if fg[fg_thiolactone]                    then mod_wrt_noln('C3SOCZ00',sc);
  if fg[fg_thiocarboxylic_acid_amide]      then mod_wrt_noln('C3SNH000',sc);
  if fg[fg_thiolactam]                     then mod_wrt_noln('C3SNCZ00',sc);
  if fg[fg_imido_thioester]                then mod_wrt_noln('C3NSC000',sc);
  if fg[fg_oxohetarene]                    then mod_wrt_noln('C3ONAZ00',sc);
  if fg[fg_thioxohetarene]                 then mod_wrt_noln('C3SNAZ00',sc);
  if fg[fg_iminohetarene]                  then mod_wrt_noln('C3NNAZ00',sc);
  if fg[fg_orthocarboxylic_acid_deriv]     then mod_wrt_noln('C3O30000',sc);
  if fg[fg_carboxylic_acid_orthoester]     then mod_wrt_noln('C3O3C000',sc);
  if fg[fg_carboxylic_acid_amide_acetal]   then mod_wrt_noln('C3O3NC00',sc);
  if fg[fg_carboxylic_acid_anhydride]      then mod_wrt_noln('C3O2C3O2',sc);
//  if fg[fg_carboxylic_acid_imide]          then mod_wrt_noln('C3ONC000',sc);
  if fg[fg_carboxylic_acid_unsubst_imide]  then mod_wrt_noln('C3ONCH10',sc);
  if fg[fg_carboxylic_acid_subst_imide]    then mod_wrt_noln('C3ONCC10',sc);
  if fg[fg_co2_deriv]                      then mod_wrt_noln('C4000000',sc);
  if fg[fg_carbonic_acid_deriv]            then mod_wrt_noln('C4O30000',sc);
  if fg[fg_carbonic_acid_monoester]        then mod_wrt_noln('C4O3C100',sc);
  if fg[fg_carbonic_acid_diester]          then mod_wrt_noln('C4O3C200',sc);
  if fg[fg_carbonic_acid_ester_halide]     then mod_wrt_noln('C4O3CX00',sc);
  if fg[fg_thiocarbonic_acid_deriv]        then mod_wrt_noln('C4SO0000',sc);
  if fg[fg_thiocarbonic_acid_monoester]    then mod_wrt_noln('C4SOC100',sc);
  if fg[fg_thiocarbonic_acid_diester]      then mod_wrt_noln('C4SOC200',sc);
  if fg[fg_thiocarbonic_acid_ester_halide] then mod_wrt_noln('C4SOX_00',sc);
  if fg[fg_carbamic_acid_deriv]            then mod_wrt_noln('C4O2N000',sc);
  if fg[fg_carbamic_acid]                  then mod_wrt_noln('C4O2NH00',sc);
  if fg[fg_carbamic_acid_ester]            then mod_wrt_noln('C4O2NC00',sc);
  if fg[fg_carbamic_acid_halide]           then mod_wrt_noln('C4O2NX00',sc);
  if fg[fg_thiocarbamic_acid_deriv]        then mod_wrt_noln('C4SN0000',sc);
  if fg[fg_thiocarbamic_acid]              then mod_wrt_noln('C4SNOH00',sc);
  if fg[fg_thiocarbamic_acid_ester]        then mod_wrt_noln('C4SNOC00',sc);
  if fg[fg_thiocarbamic_acid_halide]       then mod_wrt_noln('C4SNXX00',sc);
  if fg[fg_urea]                           then mod_wrt_noln('C4O1N200',sc);
  if fg[fg_isourea]                        then mod_wrt_noln('C4N2O100',sc);
  if fg[fg_thiourea]                       then mod_wrt_noln('C4S1N200',sc);
  if fg[fg_isothiourea]                    then mod_wrt_noln('C4N2S100',sc);
  if fg[fg_guanidine]                      then mod_wrt_noln('C4N30000',sc);
  if fg[fg_semicarbazide]                  then mod_wrt_noln('C4ON2N00',sc);
  if fg[fg_thiosemicarbazide]              then mod_wrt_noln('C4SN2N00',sc);
  if fg[fg_azide]                          then mod_wrt_noln('N4N20000',sc);
  if fg[fg_azo_compound]                   then mod_wrt_noln('N2N10000',sc);
  if fg[fg_diazonium_salt]                 then mod_wrt_noln('N3N100T2',sc);
  if fg[fg_isonitrile]                     then mod_wrt_noln('N3C10000',sc);
  if fg[fg_cyanate]                        then mod_wrt_noln('C4NO1000',sc);
  if fg[fg_isocyanate]                     then mod_wrt_noln('C4NO2000',sc);
  if fg[fg_thiocyanate]                    then mod_wrt_noln('C4NS1000',sc);
  if fg[fg_isothiocyanate]                 then mod_wrt_noln('C4NS2000',sc);
  if fg[fg_carbodiimide]                   then mod_wrt_noln('C4N20000',sc);
  if fg[fg_nitroso_compound]               then mod_wrt_noln('N2O10000',sc);
  if fg[fg_nitro_compound]                 then mod_wrt_noln('N4O20000',sc);
  if fg[fg_nitrite]                        then mod_wrt_noln('N3O20000',sc);
  if fg[fg_nitrate]                        then mod_wrt_noln('N4O30000',sc);
  if fg[fg_sulfuric_acid_deriv]            then mod_wrt_noln('S6O00000',sc);
  if fg[fg_sulfuric_acid]                  then mod_wrt_noln('S6O4H000',sc);
  if fg[fg_sulfuric_acid_monoester]        then mod_wrt_noln('S6O4HC00',sc);
  if fg[fg_sulfuric_acid_diester]          then mod_wrt_noln('S6O4CC00',sc);
  if fg[fg_sulfuric_acid_amide_ester]      then mod_wrt_noln('S6O3NC00',sc);
  if fg[fg_sulfuric_acid_amide]            then mod_wrt_noln('S6O3N100',sc);
  if fg[fg_sulfuric_acid_diamide]          then mod_wrt_noln('S6O2N200',sc);
  if fg[fg_sulfuryl_halide]                then mod_wrt_noln('S6O3XX00',sc);
  if fg[fg_sulfonic_acid_deriv]            then mod_wrt_noln('S5O00000',sc);
  if fg[fg_sulfonic_acid]                  then mod_wrt_noln('S5O3H000',sc);
  if fg[fg_sulfonic_acid_ester]            then mod_wrt_noln('S5O3C000',sc);
  if fg[fg_sulfonamide]                    then mod_wrt_noln('S5O2N000',sc);
  if fg[fg_sulfonyl_halide]                then mod_wrt_noln('S5O2XX00',sc);
  if fg[fg_sulfone]                        then mod_wrt_noln('S4O20000',sc);
  if fg[fg_sulfoxide]                      then mod_wrt_noln('S2O10000',sc);
  if fg[fg_sulfinic_acid_deriv]            then mod_wrt_noln('S3O00000',sc);
  if fg[fg_sulfinic_acid]                  then mod_wrt_noln('S3O2H000',sc);
  if fg[fg_sulfinic_acid_ester]            then mod_wrt_noln('S3O2C000',sc);
  if fg[fg_sulfinic_acid_halide]           then mod_wrt_noln('S3O1XX00',sc);
  if fg[fg_sulfinic_acid_amide]            then mod_wrt_noln('S3O1N000',sc);
  if fg[fg_sulfenic_acid_deriv]            then mod_wrt_noln('S1O00000',sc);
  if fg[fg_sulfenic_acid]                  then mod_wrt_noln('S1O1H000',sc);
  if fg[fg_sulfenic_acid_ester]            then mod_wrt_noln('S1O1C000',sc);
  if fg[fg_sulfenic_acid_halide]           then mod_wrt_noln('S1O0XX00',sc);
  if fg[fg_sulfenic_acid_amide]            then mod_wrt_noln('S1O0N100',sc);
//  if fg[fg_thiol]                          then mod_wrt_noln('S1H10000',sc);
  if fg[fg_alkylthiol]                     then mod_wrt_noln('S1H1C000',sc);
  if fg[fg_arylthiol]                      then mod_wrt_noln('S1H1A000',sc);
  if fg[fg_phosphoric_acid_deriv]          then mod_wrt_noln('P5O0H000',sc);
  if fg[fg_phosphoric_acid]                then mod_wrt_noln('P5O4H200',sc);
  if fg[fg_phosphoric_acid_ester]          then mod_wrt_noln('P5O4HC00',sc);
  if fg[fg_phosphoric_acid_halide]         then mod_wrt_noln('P5O3HX00',sc);
  if fg[fg_phosphoric_acid_amide]          then mod_wrt_noln('P5O3HN00',sc);
  if fg[fg_thiophosphoric_acid_deriv]      then mod_wrt_noln('P5O0S000',sc);
  if fg[fg_thiophosphoric_acid]            then mod_wrt_noln('P5O3SH00',sc);
  if fg[fg_thiophosphoric_acid_ester]      then mod_wrt_noln('P5O3SC00',sc);
  if fg[fg_thiophosphoric_acid_halide]     then mod_wrt_noln('P5O2SX00',sc);
  if fg[fg_thiophosphoric_acid_amide]      then mod_wrt_noln('P5O2SN00',sc);
  if fg[fg_phosphonic_acid_deriv]          then mod_wrt_noln('P4O30000',sc);
  if fg[fg_phosphonic_acid]                then mod_wrt_noln('P4O3H000',sc);
  if fg[fg_phosphonic_acid_ester]          then mod_wrt_noln('P4O3C000',sc);
  if fg[fg_phosphine]                      then mod_wrt_noln('P3000000',sc);
  if fg[fg_phosphinoxide]                  then mod_wrt_noln('P2O00000',sc);
  if fg[fg_boronic_acid_deriv]             then mod_wrt_noln('B2O20000',sc);
  if fg[fg_boronic_acid]                   then mod_wrt_noln('B2O2H000',sc);
  if fg[fg_boronic_acid_ester]             then mod_wrt_noln('B2O2C000',sc);
  if fg[fg_alkene]                         then mod_wrt_noln('000C2C00',sc);
  if fg[fg_alkyne]                         then mod_wrt_noln('000C3C00',sc);
  if fg[fg_aromatic]                       then mod_wrt_noln('0000A000',sc);
  if fg[fg_heterocycle]                    then mod_wrt_noln('0000CZ00',sc);
  if fg[fg_alpha_aminoacid]                then mod_wrt_noln('C3O2HN1C',sc);
  if fg[fg_alpha_hydroxyacid]              then mod_wrt_noln('C3O2HO1H',sc);
end;


procedure mod_wrt_noln_fg_binary;
var
  i : integer;
  n : integer;
  o : char;
begin
  for i := 1 to (max_fg div 8) do
    begin
      n := 0;
      if fg[8*i]    then inc(n);
      if fg[8*i-1]  then inc(n,2);
      if fg[8*i-2]  then inc(n,4);
      if fg[8*i-3]  then inc(n,8);
      if fg[8*i-4]  then inc(n,16);
      if fg[8*i-5]  then inc(n,32);
      if fg[8*i-6]  then inc(n,64);
      if fg[8*i-7]  then inc(n,128);
      o := chr(n);
      mod_wrt_noln(o);
    end;
end;


procedure mod_wrt_noln_fg_bitstring;
var
  i : integer;
begin
  for i := 1 to max_fg do if fg[i] then mod_wrt_noln('1') else mod_wrt_noln('0');
end;


procedure readinputfile(molfilename:string);
var
  rfile : text;
  rline : string;
begin
  molbufindex := 0;
  if not opt_stdin then
    begin
      assign(rfile,molfilename);
      reset(rfile);
      rline := '';
      while not eof(rfile) do
        begin
          readln(rfile,rline);
          mol_in_queue := false;
          if molbufindex < (max_atoms+max_bonds+8192) then
            begin
              inc(molbufindex);
              molbuf^[molbufindex] := rline;
            end else
            begin
              mod_wrt_ln('Not enough memory for molfile! ',molbufindex);
              close(rfile);
              halt(1);
            end;
        end;
      close(rfile);
    end else              // read from standard input
    begin
      rline := '';
      mol_in_queue := false;
      while (not eof) and (pos('$$$$',rline) = 0) do
        begin
          readln(rline);
          if molbufindex < (max_atoms+max_bonds+64) then
            begin
              inc(molbufindex);
              molbuf^[molbufindex] := rline;
            end else
            begin
              mod_wrt_ln('Not enough memory!');
              halt(1);
            end;
          if pos('$$$$',rline) > 0 then mol_in_queue := true;
        end;
    end;
end;


procedure copy_mol_to_needle;
var
  i, j : integer;
begin
  if (n_atoms = 0) or (n_bonds = 0) then exit;
  try
    getmem(ndl_atom,n_atoms*sizeof(atom_rec));
    getmem(ndl_bond,n_bonds*sizeof(bond_rec));
    getmem(ndl_ring,sizeof(ringlist));
  except
    on e:Eoutofmemory do
      begin
        mod_wrt_ln('Not enough memory');
        halt(4);
      end;
  end;
  ndl_n_atoms := n_atoms;
  ndl_n_bonds := n_bonds;
  ndl_n_rings := n_rings;
  ndl_n_heavyatoms := n_heavyatoms;
  ndl_n_heavybonds := n_heavybonds;
  ndl_molname := molname;
  for i := 1 to n_atoms do
    begin
      ndl_atom^[i].element        := atom^[i].element;
      ndl_atom^[i].atype          := atom^[i].atype;
      ndl_atom^[i].x              := atom^[i].x;
      ndl_atom^[i].y              := atom^[i].y;
      ndl_atom^[i].z              := atom^[i].z;
      ndl_atom^[i].formal_charge  := atom^[i].formal_charge;
      ndl_atom^[i].real_charge    := atom^[i].real_charge;
      ndl_atom^[i].Hexp           := atom^[i].Hexp;
      ndl_atom^[i].Htot           := atom^[i].Htot;
      ndl_atom^[i].neighbor_count := atom^[i].neighbor_count;
      ndl_atom^[i].ring_count     := atom^[i].ring_count;
      ndl_atom^[i].arom           := atom^[i].arom;
    end;
  for i := 1 to n_bonds do
    begin
      ndl_bond^[i].a1 := bond^[i].a1;
      ndl_bond^[i].a2 := bond^[i].a2;
      ndl_bond^[i].btype := bond^[i].btype;
      ndl_bond^[i].arom := bond^[i].arom;
    end;
  if n_rings > 0 then
    begin
      for i := 1 to n_rings do
        begin
          for j := 1 to max_ringsize do ndl_ring^[i,j] := ring^[i,j];
        end;
    end;
  with ndl_molstat do
    begin
      n_QA := molstat.n_QA; n_QB := molstat.n_QB; n_chg := molstat.n_chg;
      n_C1 := molstat.n_C1; n_C2 := molstat.n_C2; n_C := molstat.n_C;
      n_CHB1p := molstat.n_CHB1p; n_CHB2p := molstat.n_CHB2p;
      n_CHB3p := molstat.n_CHB3p; n_CHB4 := molstat.n_CHB4;
      n_O2 := molstat.n_O2; n_O3  := molstat.n_O3;
      n_N1 := molstat.n_N1; n_N2 := molstat.n_N2; n_N3 := molstat.n_N3;
      n_S := molstat.n_S; n_SeTe := molstat.n_SeTe;
      n_F := molstat.n_F; n_Cl := molstat.n_Cl; n_Br := molstat.n_Br; n_I := molstat.n_I;
      n_P := molstat.n_P; n_B := molstat.n_B;
      n_Met := molstat.n_Met; n_X := molstat.n_X;
      n_b1 := molstat.n_b1; n_b2 := molstat.n_b2; n_b3 := molstat.n_b3; n_bar := molstat.n_bar;
      n_C1O := molstat.n_C1O; n_C2O := molstat.n_C2O; n_CN := molstat.n_CN; n_XY := molstat.n_XY;
      n_r3 := molstat.n_r3; n_r4 := molstat.n_r4; n_r5 := molstat.n_r5; n_r6 := molstat.n_r6;
      n_r7 := molstat.n_r7; n_r8 := molstat.n_r8; n_r9 := molstat.n_r9; n_r10 := molstat.n_r10;
      n_r11 := molstat.n_r11; n_r12 := molstat.n_r12; n_r13p := molstat.n_r13p;
      n_rN := molstat.n_rN; n_rN1 := molstat.n_rN1; n_rN2 := molstat.n_rN2; n_rN3p := molstat.n_rN3p;
      n_rO := molstat.n_rO; n_rO1 := molstat.n_rO1; n_rO2p := molstat.n_rO2p;
      n_rS := molstat.n_rS; n_rX := molstat.n_rX;
      n_rar := molstat.n_rar;
    end;
end;


procedure get_ringstat(r_id:integer);
var
  i,j  : integer;
  testring : ringpath_type;
  ring_size : integer;
  a_ref : integer;
  elem : str2;
  nN, nO, nS, nX : integer;
begin
  nN := 0; nO := 0; nS := 0; nX := 0;
  if (r_id < 1) or (r_id > n_rings) then exit;
  fillchar(testring,sizeof(ringpath_type),0);
  for j := 1 to max_ringsize do if ring^[r_id,j] > 0 then testring[j] := ring^[r_id,j];
  ring_size := path_length(testring);
  if (ring_size > 2) then
    begin
      for i := 1 to ring_size do
        begin
          a_ref := testring[i];
          elem := atom^[a_ref].element;
          if (elem <> 'C ') and (elem <> 'A ') then 
            begin
              inc(nX);   // general heteroatom count
              if elem = 'N ' then inc(nN);
              if elem = 'O ' then inc(nO);
              if elem = 'S ' then inc(nS);
            end;
        end;
      if nN > 0 then
        begin
          inc(molstat.n_rN);
          if nN = 1 then inc(molstat.n_rN1);
          if nN = 2 then inc(molstat.n_rN2);
          if nN > 2 then inc(molstat.n_rN3p);
        end;  
      if nO > 0 then
        begin
          inc(molstat.n_rO);
          if nO = 1 then inc(molstat.n_rO1);
          if nO = 2 then inc(molstat.n_rO2p);
        end;
      if nS > 0 then inc(molstat.n_rS);
      if nX > 0 then inc(molstat.n_rX);
    end;
end;


procedure get_molstat;
var
  i : integer;
  elem  : str2;
  atype : str3;
  a1, a2 : integer;
  a1el, a2el : str2;
  btype : char;
  hbc : integer;
begin
  if n_atoms = 0 then exit;
  with molstat do
    begin
      for i := 1 to n_atoms do
        begin
          if is_heavyatom(i) then
            begin
              elem  := atom^[i].element;
              atype := atom^[i].atype;
              if (atype = 'C1 ') then inc(n_C1);
              if (atype = 'C2 ') or (atype = 'CAR') then inc(n_C2);
              if (elem  = 'C ') then inc(n_C);
              if (atype = 'O2 ') then inc(n_O2);
              if (atype = 'O3 ') then inc(n_O3);
              if (atype = 'N1 ') then inc(n_N1);
              if (atype = 'N2 ') or (atype = 'NAR') then inc(n_N2);
              if (atype = 'N3 ') or (atype = 'NPL') or (atype = 'N3+') or (atype = 'NAM') then inc(n_N3);
              if (elem = 'A ') then inc(n_QA);  // query atom
              if (elem = 'Q ') then inc(n_QA);  // query atom
              if (elem = 'S ') then inc(n_S);
              if (elem = 'SE') then inc(n_SeTe);
              if (elem = 'TE') then inc(n_SeTe);
              if (elem = 'F ') then inc(n_F);
              if (elem = 'CL') then inc(n_Cl);
              if (elem = 'BR') then inc(n_Br);
              if (elem = 'I ') then inc(n_I);
              if (elem = 'P ') then inc(n_P);
              if (elem = 'B ') then inc(n_B);
              // still missing: metals, unknown elements

              // check number of heteroatom bonds per C atom
              if elem = 'C ' then
                begin
                  hbc := hetbond_count(i);
                  if hbc >= 1 then inc(n_CHB1p);
                  if hbc >= 2 then inc(n_CHB2p);
                  if hbc >= 3 then inc(n_CHB3p);
                  if hbc  = 4 then inc(n_CHB4);
                end;
              if atom^[i].formal_charge <> 0 then 
                begin
                  inc(n_chg);
                  inc(n_charges);
                end;
            end;
        end;  // atoms
      if n_bonds > 0 then
        begin
          for i := 1 to n_bonds do
            begin
              a1 := bond^[i].a1; a2 := bond^[i].a2;
              a1el := atom^[a1].element;
              a2el := atom^[a2].element;
              btype := bond^[i].btype;
              if bond^[i].arom then inc(n_bar) else
                begin
                  if (btype = 'S') then inc(n_b1);
                  if (btype = 'D') then inc(n_b2);
                  if (btype = 'T') then inc(n_b3);
                end;
              if ((a1el = 'C ') and (a2el = 'O ')) or ((a1el = 'O ') and (a2el = 'C ')) then
                begin
                  if (btype = 'S') then inc(n_C1O);
                  if (btype = 'D') then inc(n_C2O);
                end;
              if ((a1el = 'C ') and (a2el = 'N ')) or ((a1el = 'N ') and (a2el = 'C ')) then inc(n_CN);
              if ((a1el <> 'C ') and (is_heavyatom(a1))) and ((a2el <> 'C ') and (is_heavyatom(a2))) then inc(n_XY);
            end;
        end; // bonds
      if n_rings > 0 then
        begin
          for i := 1 to n_rings do
            begin
              if is_arene(i) then inc(n_rar);
              get_ringstat(i);
            end;
        end; // rings
    end;
end;


procedure mod_wrt_noln_molstat;
begin
  with molstat do
    begin
      mod_wrt_noln('n_atoms:',n_heavyatoms,';');  // count only non-H atoms (some molfiles contain explicit H's)
      if n_bonds > 0 then mod_wrt_noln('n_bonds:',n_heavybonds,';');  // count only bonds between non-H atoms
      if n_rings > 0 then mod_wrt_noln('n_rings:',n_rings,';');
//      if n_QA    > 0 then mod_wrt_noln('n_QA:',n_QA,';');
//      if n_QB    > 0 then mod_wrt_noln('n_QB:',n_QB,';');
      if n_chg   > 0 then mod_wrt_noln('n_chg:',n_chg,';');
      if n_C1    > 0 then mod_wrt_noln('n_C1:',n_C1,';');
      if n_C2    > 0 then mod_wrt_noln('n_C2:',n_C2,';');
// requirement of a given number of sp3 carbons might be too restrictive,
// so we use the total number of carbons instead  (initially used variable n_C3 is now n_C)
      if n_C    > 0 then mod_wrt_noln('n_C:',n_C,';');
      if n_CHB1p > 0 then mod_wrt_noln('n_CHB1p:',n_CHB1p,';');
      if n_CHB2p > 0 then mod_wrt_noln('n_CHB2p:',n_CHB2p,';');
      if n_CHB3p > 0 then mod_wrt_noln('n_CHB3p:',n_CHB3p,';');
      if n_CHB4  > 0 then mod_wrt_noln('n_CHB4:',n_CHB4,';');
      if n_O2    > 0 then mod_wrt_noln('n_O2:',n_O2,';');
      if n_O3    > 0 then mod_wrt_noln('n_O3:',n_O3,';');
      if n_N1    > 0 then mod_wrt_noln('n_N1:',n_N1,';');
      if n_N2    > 0 then mod_wrt_noln('n_N2:',n_N2,';');
      if n_N3    > 0 then mod_wrt_noln('n_N3:',n_N3,';');
      if n_S     > 0 then mod_wrt_noln('n_S:',n_S,';');
      if n_SeTe  > 0 then mod_wrt_noln('n_SeTe:',n_SeTe,';');
      if n_F     > 0 then mod_wrt_noln('n_F:',n_F,';');
      if n_Cl    > 0 then mod_wrt_noln('n_Cl:',n_Cl,';');
      if n_Br    > 0 then mod_wrt_noln('n_Br:',n_Br,';');
      if n_I     > 0 then mod_wrt_noln('n_I:',n_I,';');
      if n_P     > 0 then mod_wrt_noln('n_P:',n_P,';');
      if n_B     > 0 then mod_wrt_noln('n_B:',n_B,';');
      if n_Met   > 0 then mod_wrt_noln('n_Met:',n_Met,';');
      if n_X     > 0 then mod_wrt_noln('n_X:',n_X,';');
      if n_b1    > 0 then mod_wrt_noln('n_b1:',n_b1,';');
      if n_b2    > 0 then mod_wrt_noln('n_b2:',n_b2,';');
      if n_b3    > 0 then mod_wrt_noln('n_b3:',n_b3,';');
      if n_bar   > 0 then mod_wrt_noln('n_bar:',n_bar,';');
      if n_C1O   > 0 then mod_wrt_noln('n_C1O:',n_C1O,';');
      if n_C2O   > 0 then mod_wrt_noln('n_C2O:',n_C2O,';');
      if n_CN    > 0 then mod_wrt_noln('n_CN:',n_CN,';');
      if n_XY    > 0 then mod_wrt_noln('n_XY:',n_XY,';');
      if n_r3    > 0 then mod_wrt_noln('n_r3:',n_r3,';');
      if n_r4    > 0 then mod_wrt_noln('n_r4:',n_r4,';');
      if n_r5    > 0 then mod_wrt_noln('n_r5:',n_r5,';');
      if n_r6    > 0 then mod_wrt_noln('n_r6:',n_r6,';');
      if n_r7    > 0 then mod_wrt_noln('n_r7:',n_r7,';');
      if n_r8    > 0 then mod_wrt_noln('n_r8:',n_r8,';');
      if n_r9    > 0 then mod_wrt_noln('n_r9:',n_r9,';');
      if n_r10   > 0 then mod_wrt_noln('n_r10:',n_r10,';');
      if n_r11   > 0 then mod_wrt_noln('n_r11:',n_r11,';');
      if n_r12   > 0 then mod_wrt_noln('n_r12:',n_r12,';');
      if n_r13p  > 0 then mod_wrt_noln('n_r13p:',n_r13p,';');
      if n_rN    > 0 then mod_wrt_noln('n_rN:',n_rN,';');
      if n_rN1   > 0 then mod_wrt_noln('n_rN1:',n_rN1,';');
      if n_rN2   > 0 then mod_wrt_noln('n_rN2:',n_rN2,';');
      if n_rN3p  > 0 then mod_wrt_noln('n_rN3p:',n_rN3p,';');
      if n_rO    > 0 then mod_wrt_noln('n_rO:',n_rO,';');
      if n_rO1   > 0 then mod_wrt_noln('n_rO1:',n_rO1,';');
      if n_rO2p  > 0 then mod_wrt_noln('n_rO2p:',n_rO2p,';');
      if n_rS    > 0 then mod_wrt_noln('n_rS:',n_rS,';');
      if n_rX    > 0 then mod_wrt_noln('n_rX:',n_rX,';');
      if n_rar   > 0 then mod_wrt_ln('n_rar:',n_rar,';');
    end;
end;


procedure mod_wrt_noln_molstat_X;
begin
  with molstat do
    begin
      mod_wrt_noln(n_heavyatoms,','); mod_wrt_noln(n_heavybonds,','); mod_wrt_noln(n_rings,',');
      mod_wrt_noln(n_QA,','); mod_wrt_noln(n_QB,','); mod_wrt_noln(n_chg,',');
      mod_wrt_noln(n_C1,','); mod_wrt_noln(n_C2,','); mod_wrt_noln(n_C,',');
      mod_wrt_noln(n_CHB1p,','); mod_wrt_noln(n_CHB2p,','); mod_wrt_noln(n_CHB3p,','); mod_wrt_noln(n_CHB4,',');
      mod_wrt_noln(n_O2,','); mod_wrt_noln(n_O3,',');
      mod_wrt_noln(n_N1,','); mod_wrt_noln(n_N2,','); mod_wrt_noln(n_N3,',');
      mod_wrt_noln(n_S,','); mod_wrt_noln(n_SeTe,',');
      mod_wrt_noln(n_F,','); mod_wrt_noln(n_Cl,','); mod_wrt_noln(n_Br,','); mod_wrt_noln(n_I,',');
      mod_wrt_noln(n_P,','); mod_wrt_noln(n_B,',');
      mod_wrt_noln(n_Met,','); mod_wrt_noln(n_X,',');
      mod_wrt_noln(n_b1,','); mod_wrt_noln(n_b2,','); mod_wrt_noln(n_b3,','); mod_wrt_noln(n_bar,',');
      mod_wrt_noln(n_C1O,','); mod_wrt_noln(n_C2O,','); mod_wrt_noln(n_CN,','); mod_wrt_noln(n_XY,',');
      mod_wrt_noln(n_r3,','); mod_wrt_noln(n_r4,','); mod_wrt_noln(n_r5,','); mod_wrt_noln(n_r6,',');
      mod_wrt_noln(n_r7,','); mod_wrt_noln(n_r8,','); mod_wrt_noln(n_r9,','); mod_wrt_noln(n_r10,','); 
      mod_wrt_noln(n_r11,','); mod_wrt_noln(n_r12,','); mod_wrt_noln(n_r13p,',');
      mod_wrt_noln(n_rN,','); mod_wrt_noln(n_rN1,','); mod_wrt_noln(n_rN2,','); mod_wrt_noln(n_rN3p,',');
      mod_wrt_noln(n_rO,','); mod_wrt_noln(n_rO1,','); mod_wrt_noln(n_rO2p,',');
      mod_wrt_noln(n_rS,','); mod_wrt_noln(n_rX,',');
      mod_wrt_ln(n_rar);
    end;
end;


// routines for substructure matching


function find_ndl_ref_atom: integer;
var
  i : integer;
  score : integer;
  index : integer;
  n_nb, n_hc : integer;
begin
  // finds a characteristic atom in the needle molecule,
  // i.e., one with as many substituents as possible and
  // with as many heteroatom substitutents as possible
  if ndl_n_atoms = 0 then exit;
  score := 0;
  for i := 1 to ndl_n_atoms do
    begin
      n_nb := ndl_atom^[i].neighbor_count;
      n_hc := ndl_hetatom_count(i);
      if n_nb + n_hc > score then
        begin
          index := i;
          score := n_nb + n_hc;
        end;
    end;
  find_ndl_ref_atom := index;  
end;


function atomtypes_OK(ndl_a, hst_a : integer):boolean;
var
  ndl_el : str2;
  hst_el : str2;
  ndl_nbc : integer;
  hst_nbc : integer;
  ndl_Hexp : smallint;
  hst_Htot : smallint;
  res : boolean;
begin
  if (ndl_a < 1) or (ndl_a > ndl_n_atoms) or
     (hst_a < 1) or (hst_a > n_atoms) then
     begin
       atomtypes_OK := false;
       exit;
     end;
  res := false;   
  ndl_el := ndl_atom^[ndl_a].element;
  ndl_nbc := ndl_atom^[ndl_a].neighbor_count;
  ndl_Hexp := ndl_atom^[ndl_a].Hexp;
  hst_el := atom^[hst_a].element;
  hst_nbc := atom^[hst_a].neighbor_count;
  hst_Htot := atom^[hst_a].Htot;
  if (ndl_el = hst_el) then res := true;  // very simplified...
  if (ndl_el = 'A ') and (is_heavyatom(hst_a)) then res := true;
  if ndl_el = 'Q ' then
    begin
      if (is_heavyatom(hst_a)) and (hst_el <> 'C ') then res := true;
    end;
  if ndl_el = 'X ' then
    begin
      if (hst_el = 'F ') or (hst_el = 'CL') or (hst_el = 'BR') or 
         (hst_el = 'I ') then res := true;
    end;
  // if needle atom has more substituents than haystack atom ==> no match
  if (ndl_nbc > hst_nbc) then res := false;
  // check for explicit hydrogens
  if (ndl_Hexp > hst_Htot) then res := false;
  if res then debugoutput('atom types OK ('+inttostr(ndl_a)+'/'+inttostr(hst_a)+')')
      else debugoutput('atom types not OK ('+inttostr(ndl_a)+'/'+inttostr(hst_a)+')');
  atomtypes_OK := res;
end;


function bondtypes_OK(ndl_b, hst_b : integer):boolean;
var
   ndl_arom : boolean;
   hst_arom : boolean;
   ndl_btype : char;
   hst_btype : char;
   res : boolean;
   na, ha : string;
   a1, a2 : integer;
   a1_el, a2_el : str2;
begin
   if (ndl_b < 1) or (ndl_b > ndl_n_bonds) or
      (hst_b < 1) or (hst_b > n_bonds) then
      begin
        bondtypes_OK := false;
        exit;
      end;
   res := false;
   ndl_arom := ndl_bond^[ndl_b].arom;
   ndl_btype := ndl_bond^[ndl_b].btype;
   if ndl_arom then na := '(ar)' else na := '';
   hst_arom := bond^[hst_b].arom;
   hst_btype := bond^[hst_b].btype;
   if hst_arom then ha := '(ar)' else ha := '';
   if (ndl_arom = true) and (hst_arom = true) then res := true;
   if (ndl_arom = false) and (hst_arom = false) then
     begin
       if (ndl_btype = hst_btype) then res := true;
       if (ndl_btype = 'l') and ((hst_btype = 'S') or (hst_btype = 'D')) then res := true;
       if (ndl_btype = 's') and (hst_btype = 'S') then res := true;
       if (ndl_btype = 'd') and (hst_btype = 'D') then res := true;
     end;
   // a little exception:
   if (ndl_arom = false) and (hst_arom = true) then
     begin
       if (ndl_btype = 'A') then res := true;
       if (ndl_btype = 's') or (ndl_btype = 'd') then res := true;
       if (ndl_btype = 'D') then   // added in 0.2d: do not accept C=O etc.
         begin                     // as C-O/arom
           a1 := ndl_bond^[ndl_b].a1;
           a2 := ndl_bond^[ndl_b].a2;
           a1_el := ndl_atom^[a1].element;
           a2_el := ndl_atom^[a2].element;
           if not ((a1_el = 'O ') or (a2_el = 'O ') or
                   (a1_el = 'S ') or (a2_el = 'S ') or
                   (a1_el = 'SE') or (a2_el = 'SE') or
                   (a1_el = 'TE') or (a2_el = 'TE')) then res := true;
         end;
     end;
   if (ndl_btype = 'a') then res := true;
   if res then debugoutput('bond types OK '+inttostr(ndl_b)+':'+ndl_btype+na+'/'+inttostr(hst_b)+':'+hst_btype+ha+')') else
     debugoutput('bond types not OK '+inttostr(ndl_b)+':'+ndl_btype+na+'/'+inttostr(hst_b)+':'+hst_btype+ha+')');
   bondtypes_OK := res;
end;


function matrix_OK(m:matchmatrix;ndl_dim:integer;hst_dim:integer):boolean;
var
  mr : boolean;
begin
  if (ndl_dim < 1) or (ndl_dim > 4) or (hst_dim < 1) or (hst_dim > 4) then
    begin
      matrix_OK := false;
      exit;
    end;
  mr := false;
  case ndl_dim of
    1 : case hst_dim of
          1 : begin
                if m[1,1] then mr := true;
              end;
          2 : begin
                if m[1,1] or m[1,2] then mr := true;
              end;
          3 : begin
                if m[1,1] or m[1,2] or m[1,3] then mr := true;
              end;
          4 : begin
                if m[1,1] or m[1,2] or m[1,3] or m[1,4] then mr := true;
              end;
        end;
    2 : case hst_dim of
          2 : begin
                if m[1,1] and m[2,2] then mr := true;
                if m[1,2] and m[2,1] then mr := true;
              end;
          3 : begin
                if m[1,1] and (m[2,2] or m[2,3]) then mr := true;
                if m[1,2] and (m[2,1] or m[2,3]) then mr := true;
                if m[1,3] and (m[2,1] or m[2,2]) then mr := true;
              end;
          4 : begin
                if m[1,1] and (m[2,2] or m[2,3] or m[2,4]) then mr := true;
                if m[1,2] and (m[2,1] or m[2,3] or m[2,4]) then mr := true;
                if m[1,3] and (m[2,1] or m[2,2] or m[2,4]) then mr := true;
                if m[1,4] and (m[2,1] or m[2,2] or m[2,3]) then mr := true;
              end;
        end;
    3 : case hst_dim of
          3 : begin
                if m[1,1] and m[2,2] and m[3,3] then mr := true;
                if m[1,1] and m[2,3] and m[3,2] then mr := true;
                if m[1,2] and m[2,1] and m[3,3] then mr := true;
                if m[1,2] and m[2,3] and m[3,1] then mr := true;
                if m[1,3] and m[2,1] and m[3,2] then mr := true;
                if m[1,3] and m[2,2] and m[3,1] then mr := true;
              end;
          4 : begin
                if m[1,1] and m[2,2] and (m[3,3] or m[3,4]) then mr := true;
                if m[1,1] and m[2,3] and (m[3,2] or m[3,4]) then mr := true;
                if m[1,1] and m[2,4] and (m[3,2] or m[3,3]) then mr := true;
                if m[1,2] and m[2,1] and (m[3,3] or m[3,4]) then mr := true;
                if m[1,2] and m[2,3] and (m[3,1] or m[3,4]) then mr := true;
                if m[1,2] and m[2,4] and (m[3,1] or m[3,3]) then mr := true;
                if m[1,3] and m[2,1] and (m[3,2] or m[3,4]) then mr := true;
                if m[1,3] and m[2,2] and (m[3,1] or m[3,4]) then mr := true;
                if m[1,3] and m[2,4] and (m[3,1] or m[3,2]) then mr := true;
                if m[1,4] and m[2,1] and (m[3,2] or m[3,3]) then mr := true;
                if m[1,4] and m[2,2] and (m[3,1] or m[3,3]) then mr := true;
                if m[1,4] and m[2,3] and (m[3,1] or m[3,2]) then mr := true;
              end;
        end;
    4 : case hst_dim of
          4 : begin
                if m[1,1] and m[2,2] and ((m[3,3] and m[4,4]) or (m[3,4] and m[4,3])) then mr := true;
                if m[1,1] and m[2,3] and ((m[3,2] and m[4,4]) or (m[3,4] and m[4,2])) then mr := true;
                if m[1,1] and m[2,4] and ((m[3,2] and m[4,3]) or (m[3,3] and m[4,2])) then mr := true;
                if m[1,2] and m[2,1] and ((m[3,3] and m[4,4]) or (m[3,4] and m[4,3])) then mr := true;
                if m[1,2] and m[2,3] and ((m[3,1] and m[4,4]) or (m[3,4] and m[4,1])) then mr := true;
                if m[1,2] and m[2,4] and ((m[3,1] and m[4,3]) or (m[3,3] and m[4,1])) then mr := true;
                if m[1,3] and m[2,1] and ((m[3,2] and m[4,4]) or (m[3,4] and m[4,2])) then mr := true;
                if m[1,3] and m[2,2] and ((m[3,1] and m[4,4]) or (m[3,4] and m[4,1])) then mr := true;
                if m[1,3] and m[2,4] and ((m[3,1] and m[4,2]) or (m[3,2] and m[4,1])) then mr := true;
                if m[1,4] and m[2,1] and ((m[3,2] and m[4,3]) or (m[3,3] and m[4,2])) then mr := true;
                if m[1,4] and m[2,2] and ((m[3,1] and m[4,3]) or (m[3,3] and m[4,1])) then mr := true;
                if m[1,4] and m[2,3] and ((m[3,1] and m[4,2]) or (m[3,2] and m[4,1])) then mr := true;
              end;
        end;
  end; // case
  matrix_OK := mr;
end;


function is_matching(ndl_xmp:matchpath_type;hst_xmp:matchpath_type):boolean;
var
  i, j : integer;
  ndl_n_nb : integer;
  n_nb : integer;
  ndl_a : integer;
  hst_a : integer;
  ndl_b : integer;
  hst_b : integer;
  prev_ndl_a : integer;
  prev_hst_a : integer;
  next_ndl_a : integer;
  next_hst_a : integer;
  ndl_nb : neighbor_rec;
  hst_nb : neighbor_rec;
  mm : matchmatrix;  
  ndl_mp_len : integer;
  hst_mp_len : integer;
  ndl_mp : matchpath_type;
  hst_mp : matchpath_type;
  emptyline : boolean;
  res : boolean;
  tmpstr : string;
begin
  // initialize local matchpath variables
  fillchar(ndl_mp,sizeof(matchpath_type),0);
  fillchar(hst_mp,sizeof(matchpath_type),0);
  // copy content of external variables into local ones
  for i := 1 to max_matchpath_length do
    begin
      ndl_mp[i] := ndl_xmp[i];
      hst_mp[i] := hst_xmp[i];
    end;
  ndl_mp_len := matchpath_length(ndl_mp);
  hst_mp_len := matchpath_length(hst_mp);
  if ndl_mp_len <> hst_mp_len then
    begin
      // this should never happen....
      debugoutput('needle and haystack matchpaths are of different length');
      is_matching := false;
      exit;
    end;  
  ndl_a := ndl_mp[ndl_mp_len];
  hst_a := hst_mp[hst_mp_len];
  ndl_b := 0;
  hst_b := 0;
  prev_ndl_a := 0;
  prev_hst_a := 0;
  if ndl_mp_len > 1 then
    begin
      prev_ndl_a := ndl_mp[ndl_mp_len-1];
      prev_hst_a := hst_mp[hst_mp_len-1];
    end;
  // check whatever can be checked as early as now:
  // e.g. different elements or more substituents on needle atom than on haystack
  if (not atomtypes_OK(ndl_a, hst_a)) then
     begin
       is_matching := false;
       exit;
     end;
  // positive scenarios:
  ndl_b := get_ndl_bond(prev_ndl_a,ndl_a);
  hst_b := get_bond(prev_hst_a,hst_a);
  debugoutput('Now checking atoms '+inttostr(ndl_a)+'/'+inttostr(hst_a)+', bonds '+inttostr(ndl_b)+'/'+inttostr(hst_b));
  if (ndl_b > 0) and (hst_b > 0) then
    begin
      // do a quick check if bond types match
      if (not bondtypes_OK(ndl_b, hst_b)) then
        begin
          debugoutput('  failed match of bonds '+inttostr(ndl_b)+'/'+inttostr(hst_b));
          is_matching := false;
          exit;
        end;
    end;
  // a) we reached the end of our needle fragment (and atom/bond types match)
  if (ndl_atom^[ndl_a].neighbor_count = 1) and (atomtypes_OK(ndl_a, hst_a)) and
     (bondtypes_OK(ndl_b, hst_b)) then
     begin
       is_matching := true;
       debugoutput('  ==> end of needle fragment at atom '+inttostr(ndl_a)+' (match)');
       exit;
     end;    
  // b) a ring is formed (ndl_a is already in the path) and atom/bond types match
  if (matchpath_pos(ndl_a,ndl_mp) > 0) and (matchpath_pos(ndl_a,ndl_mp) < matchpath_length(ndl_mp)) then
    begin
      if (matchpath_pos(ndl_a,ndl_mp) = matchpath_pos(hst_a, hst_mp)) and
         (atomtypes_OK(ndl_a, hst_a)) and
         (bondtypes_OK(ndl_b, hst_b)) then      
         begin
           is_matching := true;
           debugoutput('matchpath forms ring at: '+inttostr(ndl_a)+' (match)');
           exit;
         end else
         begin
           is_matching := false;
           debugoutput('matchpath forms ring at: '+inttostr(ndl_a)+' (no match)');
           exit;
         end;
    end;
  // in all other cases: do the hard work:
  // first, get all heavy-atom neighbors of needle and haystack;
  // at the beginning of the search, this means all neighbors, then it means
  // all but the previous atom (where we came from)
  fillchar(ndl_nb, sizeof(neighbor_rec),0);
  fillchar(hst_nb, sizeof(neighbor_rec),0);
  if (matchpath_length(ndl_mp) = 1) then
    begin
      ndl_n_nb := ndl_atom^[ndl_a].neighbor_count;
      n_nb := atom^[hst_a].neighbor_count;
      ndl_nb := get_ndl_neighbors(ndl_a);
      hst_nb := get_neighbors(hst_a);
    end else
    begin
      ndl_n_nb := ndl_atom^[ndl_a].neighbor_count -1;
      n_nb := atom^[hst_a].neighbor_count -1;
      ndl_nb := get_ndl_nextneighbors(ndl_a,prev_ndl_a);
      hst_nb := get_nextneighbors(hst_a,prev_hst_a);
    end;
  // now that the neighbor-arrays are filled, get all
  // combinations of matches recursively;
  // first, initialize the match matrix
  for i := 1 to 4 do
    for j := 1 to 4 do mm[i,j] := false;
  // make sure there are not too many neighbors (max. 4)
  if (ndl_n_nb > 4) or (n_nb > 4) then
    begin
      debugoutput('too many neighbors - exiting');
      is_matching := false;
      exit;
    end;
  // check if matchpath is not already filled up
  if matchpath_length(ndl_mp) = max_matchpath_length then
    begin
      debugoutput('matchpath too long - exiting');
      is_matching := false;
      exit;
    end;
  // next, check which chain of the needle matches which chain of the haystack 
  for i := 1 to ndl_n_nb do
    begin
      emptyline := true;
      next_ndl_a := ndl_nb[i];
      for j := 1 to n_nb do
        begin
          next_hst_a := hst_nb[j];
          ndl_mp[ndl_mp_len+1] := next_ndl_a;
          hst_mp[hst_mp_len+1] := next_hst_a;
          if is_matching(ndl_mp,hst_mp) then
            begin
              mm[i,j] := true; 
              emptyline := false;
            end;
        end;
      // if a needle substituent does not match any of the haystack substituents,
      // stop any further work immediately
      if emptyline then 
        begin
          is_matching := false;
          exit;
        end;  
    end; 
  // finally, check the content of the matrix
  res := matrix_OK(mm,ndl_n_nb,n_nb);
  is_matching := res;
  if res then tmpstr := ' MATCH' else tmpstr := ' NO MATCH';
  debugoutput('result for atoms '+inttostr(ndl_a)+'/'+inttostr(hst_a)+', bonds '+inttostr(ndl_b)+'/'+inttostr(hst_b)+':'+tmpstr);
end;

function quick_match:boolean;  // added in v0.2c
var
  res : boolean;
  i : integer;
begin
  if ((ndl_n_atoms < 1) or (n_atoms < 1)) or 
     ((ndl_n_atoms > n_atoms) or (ndl_n_bonds > n_bonds)) then
    begin   // just to be sure...
      quick_match := false;
      exit;
    end;
  res := true;
  for i := 1 to ndl_n_atoms do
    begin
      if atom^[i].atype <> ndl_atom^[i].atype then res := false;
    end;
  if ndl_n_bonds > 0 then
    begin
      for i := 1 to ndl_n_bonds do
        begin
          if (ndl_bond^[i].a1 <> bond^[i].a1) or
             (ndl_bond^[i].a2 <> bond^[i].a2) or
             (ndl_bond^[i].btype <> bond^[i].btype) then res := false;
        end;
    end;
  quick_match := res;
end;


procedure perform_match;
var
  i, j : integer;
  ndl_ref_atom : integer;
  ndl_n_nb : integer;
  ndl_n_hc : integer;
  n_nb : integer;
  n_hc : integer;
begin
  // if we perform an exact match, needle and haystack must have
  // the same number of atoms, bonds, and rings
  if opt_exact then
    begin
      if (n_heavyatoms <> ndl_n_heavyatoms) or (n_heavybonds <> ndl_n_heavybonds) then 
        begin
          matchresult := false;
          debugoutput('different number of heavy atoms and/or bonds');
          exit;
        end;
    end;
  // have a quick look iof needle and haystack are identical molfiles
  if quick_match then
    begin
      matchresult := true;
      exit;
    end;
  // first, look for a characteristic atom in the needle
  ndl_ref_atom := find_ndl_ref_atom;  
  debugoutput('needle reference atom: '+inttostr(ndl_ref_atom)+' ('+ndl_atom^[ndl_ref_atom].atype+')');
  ndl_n_nb := ndl_atom^[ndl_ref_atom].neighbor_count;
  ndl_n_hc := ndl_hetatom_count(ndl_ref_atom);
  debugoutput('neighbor atoms: '+inttostr(ndl_n_nb)+'  heteroatom neighbors: '+inttostr(ndl_n_hc));
  matchresult := false;
  for j := 1 to max_matchpath_length do
    begin
      ndl_matchpath[j] := 0;
      hst_matchpath[j] := 0;
    end;
  ndl_matchpath[1] := ndl_ref_atom;
  i := 0;
  while (i < n_atoms) and (matchresult = false) do
    begin
      inc(i);
      n_nb := atom^[i].neighbor_count;
      n_hc := hetatom_count(i);
      if (n_nb >= ndl_n_nb) and (n_hc >= ndl_n_hc) then
        begin
          debugoutput('trying atom '+inttostr(i)+'; neighbor atoms: '+inttostr(n_nb)+' heteroatom neighbors: '+inttostr(n_hc));
          hst_matchpath[1] := i;
          matchresult := is_matching(ndl_matchpath, hst_matchpath);
          if matchresult then debugoutput('matching atom in haystack: '+inttostr(i)+' ('+atom^[i].atype+')');
        end;
    end;
end;


procedure clear_rings;
var
  i : integer;
begin
  n_rings := 0;
  fillchar(ring^, sizeof(ringlist),0);
  if n_atoms > 0 then
    begin
      for i := 1 to n_atoms do atom^[i].ring_count := 0;
    end;
  if n_bonds > 0 then
    begin
      for i := 1 to n_bonds do bond^[i].ring_count := 0;
    end;
end;


function ring_lastpos(s:ringpath_type):integer;
var
  i, j, rc, rlp : integer;
begin
  rlp := 0;
  if n_rings > 0 then
    begin
      for i := 1 to n_rings do
        begin
          rc := ringcompare(s, ring^[i]);
          if rc_identical(rc) then rlp := i;
        end;
    end;
  ring_lastpos := rlp;
end;


procedure remove_redundant_rings;
var
  i, j, k, rlp : integer;
  tmp_path : ringpath_type;
begin
  if n_rings < 2 then exit;
  for i := 1 to (n_rings - 1) do
    begin
      tmp_path := ring^[i];
      rlp := ring_lastpos(tmp_path);
      while rlp > i do
        begin
          debugoutput('removing redundant ring: '+inttostr(rlp)+' (identical to ring '+inttostr(i)+')');
          for j := rlp to (n_rings - 1) do ring^[j] := ring^[(j+1)];
          for k := 1 to max_ringsize do ring^[n_rings,k] := 0;
          dec(n_rings);
          rlp := ring_lastpos(tmp_path);
        end;
    end;
end;

{*
begin  // main routine
  progname := extractfilename(paramstr(0));
  if pos('MATCHMOL',upcase(progname))>0 then progmode := pmMatchMol else
    begin
      if pos('CHECKMOL',upcase(progname))>0 then progmode := pmCheckMol else
        begin
          mod_wrt_ln('THOU SHALLST NOT RENAME ME!');
          halt(9);
        end;
    end;
  if (paramcount = 0) then
    begin
      show_usage;
      halt(1);
    end;
  init_globals;
  init_molstat(molstat);
  parse_args;
  if ringsearch_mode = rs_sar then max_vringsize := max_ringsize else
                                   max_vringsize := 10;
  if opt_verbose then mod_wrt_ln(progname+' v',version,'  N. Haider 2003');
  if progmode = pmMatchMol then
    begin
      if (not fileexists(ndl_molfilename))  and (not opt_stdin) then
        begin
          mod_wrt_ln('file ',ndl_molfilename,' not found!');
          halt(2);
        end;
    end;
  if (not fileexists(molfilename))  and (not opt_stdin) then
    begin
      mod_wrt_ln('file ',molfilename,' not found!');
      halt(2);
    end;
  // read the first molecule and process it; if we are in "matchmol" mode,
  // this is the "needle"
  if progmode = pmMatchMol then readinputfile(ndl_molfilename) else
                                readinputfile(molfilename);
  li := 1;   // initialize line pointer for input buffer
  filetype := get_filetype(ndl_molfilename);
  if filetype = 'unknown' then
    begin
      mod_wrt_ln('unknown file format!');
      if opt_verbose then
        begin
          mod_wrt_ln('===========================================');
          for i := 1 to molbufindex do mod_wrt_ln(molbuf^[i]);
        end;
      halt(3);
    end;
  if filetype = 'alchemy' then read_molfile(ndl_molfilename);
  if filetype = 'sybyl'   then read_mol2file(ndl_molfilename);
  if filetype = 'mdl'     then read_MDLmolfile(ndl_molfilename);
  count_neighbors;
  if (not found_arominfo) or (progmode = pmCheckMol) then  // added in v0.2b/0.2c
    begin
      debugoutput('no aromaticity information found - checking myself...');
      chk_ringbonds;
      if ringsearch_mode = rs_ssr then remove_redundant_rings;
      if n_rings = max_rings then
        begin
          if opt_verbose then mod_wrt_ln('warning: max. number of rings exceeded, reverting to SSR search');
          ringsearch_mode := rs_ssr;
          clear_rings;
          max_vringsize := 10;
          chk_ringbonds;
          remove_redundant_rings;
        end;
      update_ringcount;
      update_atypes;
      chk_arom;
    end else 
    begin
      debugoutput('found aromaticity information in input file');
      update_Htotal;  // end v0.2b snippet
    end;
  if progmode = pmCheckMol then
    begin
      if opt_verbose   then mod_wrt_noln_mol;
      get_molstat;
      if opt_molstat then
        begin
          if opt_molstat_X then mod_wrt_noln_molstat_X else mod_wrt_noln_molstat;
        end else
        begin
          if found_querymol then
            begin
              mod_wrt_ln('input structure contains query atom or query bond!');
              halt(1);
            end;
          chk_functionalgroups;
          if opt_none      then opt_text := true;
          if opt_text      then mod_wrt_noln_fg_text;
          if opt_text_de   then mod_wrt_noln_fg_text_de;
          if opt_code      then mod_wrt_noln_fg_code;
          if opt_bin       then mod_wrt_noln_fg_binary;
          if opt_bitstring then mod_wrt_noln_fg_bitstring;
          if opt_xmdlout   then mod_wrt_noln_MDLmolfile;
        end;
      zap_molecule;
    end else
    begin  // progmode = pmMatchMol
      // now transfer all data to the "needle" set of variables
      copy_mol_to_needle;
      if opt_verbose then mod_wrt_noln_needle_mol;
      mol_count := 0;
      // next, read the "haystack" file and process it
      li := 1;
      repeat
        begin
          if opt_stdin or (mol_count = 0) then
            begin
              readinputfile(molfilename);
              li := 1;
            end;
          filetype := get_filetype(molfilename);
          if filetype <> 'unknown' then
            begin
              found_arominfo := false;  // added in v0.2b
              if filetype = 'alchemy' then read_molfile(molfilename);
              if filetype = 'sybyl'   then read_mol2file(molfilename);
              if filetype = 'mdl'     then read_MDLmolfile(molfilename);
              inc(mol_count);
              count_neighbors;
              if not found_arominfo then
                begin
                  debugoutput('no aromaticity information found - checking myself...');
                  chk_ringbonds;
                  if ringsearch_mode = rs_ssr then remove_redundant_rings;
                  if n_rings = max_rings then
                    begin
                      if opt_verbose then mod_wrt_ln('warning: max. number of rings exceeded, reverting to SSR search');
                      ringsearch_mode := rs_ssr;
                      clear_rings;
                      max_vringsize := 10;
                      chk_ringbonds;
                      remove_redundant_rings;
                    end;
                  update_ringcount;
                  update_atypes;
                  chk_arom;
                end else 
                begin
                  debugoutput('found aromaticity information in input file');
                  update_Htotal;
                end;
              init_molstat(ndl_molstat);
              if opt_verbose then mod_wrt_noln_mol;
              // now that we have both molecules, perform the comparison
              perform_match;
              if matchresult = true then
                begin
                  if opt_molout then
                    begin
                      for i := 1 to molbufindex do mod_wrt_ln(molbuf^[i]);
                    end else mod_wrt_ln(inttostr(mol_count),':T');
                end else
                begin
                  if not opt_molout then mod_wrt_ln(inttostr(mol_count),':F');
                end;
              zap_molecule;
              if opt_stdin then molbufindex := 0;
            end;  // else mod_wrt_ln(mol_count,':unknown file format'); // if filetype <> 'unknown'
        end;
        //mod_wrt_ln(li,'/',molbufindex,': ',molbuf^[li]);
      until (mol_in_queue = false) or ((opt_stdin = false) and (li >= molbufindex));
      zap_needle;
    end;
    *}
{*******************************************************************************}
{*   ******     **         **           ****************************************}
{*   *******    **         **           ****************************************}
{*   **     **  **         **           ****************************************}
{*   **     **  **         **           ****************************************}
{*   **     **  **         **           ****************************************}
{*   **     **  **         **           ****************************************}
{*   **     **  **         **           ****************************************}
{*   **     **  **         **           ****************************************}
{*   ********   *********  *********    ****************************************}
{*   ******     *********  *********    ****************************************}
{*******************************************************************************}

procedure init_globals_DLL;
var
  i : integer;
begin
  opt_verbose     := false;
  opt_debug       := false;
  opt_exact       := false;
  opt_stdin       := false;
  opt_text        := false;
  opt_code        := false;
  opt_bin         := false;
  opt_bitstring   := false;
  opt_molout      := false;
  opt_molstat     := false;
  opt_molstat_X   := false;
  opt_xmdlout     := false;
  cm_mdlmolfile   := false;
  found_arominfo  := false;
  found_querymol  := false;
  ringsearch_mode := rs_sar;
  for i := 1 to max_fg do fg[i] := false;

if yet_initialized = false then
begin
  try
    getmem(molbuf,sizeof(molbuftype));
  except
    on e:Eoutofmemory do
      begin
        messagebox(0,'init_globals_dll error','ERROR',0);
        mod_wrt_ln('Not enough memory');
        halt(4);
      end;
  end;
yet_initialized:=true;
end;
end;


procedure mm_InitMol();
begin

  init_globals_dll;
  init_molstat(molstat);
  if ringsearch_mode = rs_sar then max_vringsize := max_ringsize else
                                   max_vringsize := 10;
  zap_molecule;
  molbufindex := 0;

end;

procedure mm_ElabMol();
begin
 li := 1;   // initialize line pointer for input buffer
  filetype := get_filetype(ndl_molfilename);
  if filetype = 'unknown' then
    begin
      messagebox (0,'Error in mm_ElabMol: Unknown file format','MATCHMOLDLL ERROR',0);
      halt(3);
    end;
  if filetype = 'alchemy' then read_molfile(ndl_molfilename);
  if filetype = 'sybyl'   then read_mol2file(ndl_molfilename);
  if filetype = 'mdl'     then read_MDLmolfile(ndl_molfilename);
  count_neighbors;
  chk_ringbonds;
  if ringsearch_mode = rs_ssr then remove_redundant_rings;
  if n_rings = max_rings then
    begin
      if opt_verbose then mod_wrt_ln('warning: max. number of rings exceeded, reverting to SSR search');
      ringsearch_mode := rs_ssr;
      clear_rings;
      max_vringsize := 10;
      chk_ringbonds;
      remove_redundant_rings;
    end;
  update_ringcount;
  update_atypes;
  chk_arom;

end;

procedure mm_SetCurrentMolAsQuery(); export;stdcall;
begin
zap_needle;
//mm_ElabMol;

 copy_mol_to_needle;
 molbufindex := 0;
 mol_count:=0;
end;

function mm_Match(ExactMatch:bool):integer; export;stdcall;
begin
     mm_match:=0;

     opt_exact:=ExactMatch;
     mol_count:=1;
//     mm_ElabMol;
     init_molstat(ndl_molstat);
     perform_match;


     mol_count:=0;
     if matchresult then mm_Match:=1;
     zap_molecule;
     molbufindex := 0;
end;

procedure mm_ReadInputLine(st:Pchar);
begin

          mol_in_queue := false;
          if molbufindex < (max_atoms+max_bonds+8192) then
            begin
              inc(molbufindex);
              molbuf^[molbufindex] := string(st);

            end else
            begin
              messagebox(0,'Error in mm_Readinputline; memory problem','ERROR',0);
              mod_wrt_ln('Not enough memory for molfile! ',molbufindex);
              halt(1);
            end;
end;

procedure mm_SetMol(st:pchar);export;stdcall;
var
bb:char;
aa:char;
i:integer;
k:integer;
J:integer;
spt:integer;
tt:pChar;
bb10:char;
bb13:char ;
bb0:char;
lenst:integer;
d:string;
begin
bb10:=chr(10);
bb0:=chr(0)   ;
bb13:=chr(13) ;

lenst:=strlen(st);
tt:=stralloc(1024);
//messagebox(0,st,'',0);

 spt:=0 ;
mm_InitMol;

for i := spt to lenst do
begin
     bb:=st[i];

               if ((bb=bb10) or (i=lenst)) then
                  begin
                   J:=0;
                   // d:='';
                   for k := spt to i  do
                   begin
                   aa:=st[k];
                   if ((aa<>bb10)and(aa<>bb13)) then
                      begin
                      //d:=d+aa;
                      tt[J]:=aa;
                      inc(J);
                      end;
                   end;
                   tt[J]:=bb0;
                   spt:=i;
                   //messagebox (0,tt,tt,0);
                   mm_readinputline(tt);
                  end;
end;
strdispose(tt);
mm_elabmol;
end;


function mm_GetRings():integer;export;stdcall;
begin
mm_getrings:=n_rings;
end;




Function mm_GetAtomRing(AtomNumber:integer):integer;export;stdcall;
var
  i, j, a1, a2, b, pl : integer;
begin
a1:= atom^[AtomNumber].ring_count;
mm_getatomring:= 0;
  if n_rings > 0 then
    begin
      for i := 1 to n_rings do
        begin
          pl := path_length(ring^[i]);
//          a2 := ring^[i,pl];
          for j := 1 to pl do
            begin
            a1 := ring^[i,j];
            if AtomNumber = a1 then mm_getatomring:=i;
//
//              inc(atom^[a1].ring_count);
//              b := get_bond(a1,a2);
//              inc(bond^[b].ring_count);
//              a2 := a1;
            end;
        end;
    end;
end;

Procedure mm_Version(st:pchar);export;stdcall;
begin
strpcopy(st,version);
end;


 exports mm_SetMol;
 exports mm_GetAtomRing;
 exports mm_SetCurrentMolAsQuery;
 exports mm_Match;
 exports mm_GetRings;
 exports mm_Version;









end.

